# Proxies.sx > Mobile and residential proxy bandwidth for AI agents, bought and driven from a terminal: a dedicated 4G/5G modem rented per day, week or month (unlimited traffic), metered GB on the Pool Gateway (`gw.proxies.sx:7000` HTTP / `:7001` SOCKS5, one credential for every country), or account-less USDC via x402. REST API with `X-API-Key: psx_...`, an MCP server, or plain curl. Base URL `https://api.proxies.sx/v1`. Last verified: 2026-10-28 against the live site and the backend source. Precedence: the live API wins over every file here; a product skill.md wins over this index; this index wins over page copy. ## If you want to BUY Pick the path by the shape of the need, then read only that file. - **(A) Private modem: one dedicated 4G/5G modem, unlimited traffic, per day / week / month.** Offer this FIRST for any dedicated, port, exclusive-device, p0f (OS fingerprint), OpenVPN or unlimited-traffic request. Own HTTP and SOCKS5 host:port, OpenVPN profile, rotate on demand (API or a public link), auto-renew. Paid from the account balance; the price per plan is live in `GET /v1/private-modems/catalog` and is never hard-coded here. Buying needs an API key with the `modems:purchase` scope. Contract: https://agents.proxies.sx/modems/skill.md - quickstart: https://agents.proxies.sx/quickstart/buy-modem.md - **(B) GB on the Pool Gateway: metered bandwidth, one credential, every country.** $4/GB, volume discounts 10/20/30/40% at 25/50/100/250 GB in one purchase ($2.40/GB at 250 GB+), duration free, same price on the `peer` and `mbl` pools. While country tiers are live (`pricing.live` in `GET /v1/gateway/pool/stock`), a GB of traffic uses 1.00 / 0.375 / 0.1875 GB of balance by exit country (Premium $4, Plus $1.50, Standard $0.75 per GB of traffic at the base price). From 1 TB on the peer pool, volume deals go to $0.50/GB by quote only, not a checkout price: contact maya@proxies.sx with the committed volume and countries. Contract: https://agents.proxies.sx/pool/skill.md - quickstart: https://agents.proxies.sx/quickstart/buy-gb.md - **(C) x402: USDC on Base or Solana, no account, no API key.** `GET https://api.proxies.sx/v1/x402/pool?tier=peer&country=us&traffic=1` answers HTTP 402 with the exact payment; pay, retry with `Payment-Signature: `, receive a ready credential. $4 per GB of traffic in every country (no country tiers), minimum 0.1 GB. A dedicated port is also sold this way at `/v1/x402/proxy`. Contract: https://agents.proxies.sx/pool/skill.md (x402 section) and https://agents.proxies.sx/.well-known/x402.json What a key can and cannot do (so you never promise more): - An account needs one human step: the signup email carries a verification link, and `POST /v1/login/signin` answers 401 until it is clicked (or `GET /v1/login/verify-email?token=...` is called with the token from that link). - API keys are created by a human in the dashboard (https://client.proxies.sx/account, API keys, tick the scopes) or with a dashboard JWT at `POST /v1/api-keys`. A key cannot create or edit keys. `modems:purchase` and `billing:topup` are opt-in and never in the default set; a key without them gets 403 `scope_missing` naming the scope. - Card top-up by API: `POST /v1/stripe/create-balance-checkout {"amount": 20}` with a `billing:topup` key returns a Stripe Checkout `url` that a HUMAN opens and pays; it is not headless. Per-key daily cap `spendCapUsdPerDay`, default 100 USD (403 `spend_cap_reached` above it). Poll `GET /v1/stripe/balance-checkout//receipt` until `credited: true`. Quickstart: https://agents.proxies.sx/quickstart/topup.md - A customer API key cannot buy GB. Balance becomes GB in the dashboard (https://client.proxies.sx/purchase) or with a dashboard JWT at `POST /v1/billing/purchase-traffic`; reseller and admin keys may automate it. Everything after that (proxy password, credentials, stock, usage) is key-drivable. - Every spend carries an `Idempotency-Key` and the `expectedPriceUsd` you were shown; 409 `price_changed` means re-read the price and confirm with the user. Every money step needs the user's approval of the amount. - Peer exit IPs are never published. Stock endpoints return counts per country only; examples mask addresses as `x.x`. ## Prices and errors (machine-readable) - [pricing.json](https://agents.proxies.sx/pricing.json): the GB price table generated from the backend source (base price, volume tiers, country tiers, the 1 TB quote line, payment routes), in the shape of the live `GET https://api.proxies.sx/v1/pricing`, which wins when they disagree - [Live pool stock and country tiers](https://api.proxies.sx/v1/gateway/pool/stock): counts per pool and country plus the `pricing` block (`live`, `basePricePerGB`, `volumeDiscounts`, `tiers[]` with `burnRate`, `pricePerGB`, `countries`), no auth, never an IP - [Live x402 pricing](https://api.proxies.sx/v1/x402/pricing): the $4/GB base and volume discounts for wallet purchases, no auth - [Error table](https://agents.proxies.sx/errors.md): every gateway `E_*` code, purchase-API code and the x402 402 body with HTTP status, meaning, exact fix and whether a retry helps ## Quickstarts (curl first, every step verified) - [Buy GB and connect](https://agents.proxies.sx/quickstart/buy-gb.md): sign up, verify the email, key, top up, buy GB in the dashboard, proxy password, connect through `gw.proxies.sx:7000` - [Rent a private modem](https://agents.proxies.sx/quickstart/buy-modem.md): catalog, quote, buy with `Idempotency-Key` and `expectedPriceUsd`, poll, connect, rotate, renew - [Top up the balance by API](https://agents.proxies.sx/quickstart/topup.md): checkout link, human pays, poll the receipt, balance credited - [Pool quickstart (HTML)](https://agents.proxies.sx/pool-quickstart.html): the shortest path to a working pool credential - [Rotation cookbook](https://agents.proxies.sx/pool/rotation-cookbook.md): copy-paste IP rotation in curl/python/node and the footguns that trip LLMs; read before writing rotation code ## Product contracts (one skill.md per product) - [Private modems](https://agents.proxies.sx/modems/skill.md): catalog, quote, buy, poll, credentials (HTTP, SOCKS5, OpenVPN), rotate, auto-rotate, renew, auto-renew, OS fingerprint, replace; page https://agents.proxies.sx/modems/ - [Pool Gateway](https://agents.proxies.sx/pool/skill.md): API-key path, deposited GB, country tiers, username DSL, stock, sessions, errors, the x402 alternative; page https://agents.proxies.sx/pool/ - [Dedicated port (legacy)](https://agents.proxies.sx/marketplace/skill.md): the older per-port modem pool (`POST /v1/ports`, `/v1/x402/proxy`) in US, GB, FR, NL, PL, GE (Georgia); nearly full, so start at https://agents.proxies.sx/modems/ - [Private Pool](https://agents.proxies.sx/private-pool/skill.md): named country-scoped credentials and Reserved IPs held exclusively for you - [Task market](https://agents.proxies.sx/tasks/skill.md): wallet-only x402 catalog of bounded egress tasks; the 402 response is the quote - [Master skill file](https://agents.proxies.sx/skill.md): the whole machine contract in one file (buy steps, x402 402 shape, username DSL, MCP catalog, peer protocol, compute, reseller keys) - [Agent research index](https://agents.proxies.sx/index.md): one screen, every page and which question it answers ## Use it from a tool - [MCP server](https://agents.proxies.sx/mcp/): `npx -y @proxies-sx/mcp-server` with `PROXIES_API_KEY=psx_...`. Version 3.1.0 (release pending; npm currently 3.0.1) exposes a default toolset of 20 tools: buy (`proxies_modem_catalog`, `proxies_modem_quote`, `proxies_modem_buy`, `proxies_modem_renew`, `proxies_topup_checkout`, `proxies_topup_status`, `get_pricing`), use (`pool_get_stock`, `pool_get_my_credentials`, `pool_build_proxy_url`, `pool_explain_error`, `proxies_modem_mine`, `proxies_modem_rotate`) and manage (account, usage, proxy password, status, tickets). The full set (96 API-key tools plus 16 wallet tools) loads with `--toolset=full` or `PROXIES_TOOLSET=full`. Wallet-only x402 mode with `AGENT_WALLET_KEY`. Call `tools/list` for the authoritative catalog. npm: https://www.npmjs.com/package/@proxies-sx/mcp-server - source: https://github.com/bolivian-peru/proxies-sx-mcp-server - [Integrations](https://agents.proxies.sx/integrations/skill.md): Playwright, browser-use, LangChain, CrewAI, n8n, Make/Zapier, OpenAI Agents SDK, Vercel AI SDK, Stagehand, CloakBrowser (https://agents.proxies.sx/integrations/cloakbrowser/) - [Pool Gateway Swagger](https://api.proxies.sx/docs/gateway): public OpenAPI for the gateway surface; the reseller API is at https://api.proxies.sx/v1/reseller/docs/openapi ## Gateway username (the thing agents get wrong) `psx_--[-rot-][-sid-]`, password = your proxy password (set once with `PATCH /v1/account/proxy-password`; the account login password is never accepted). `pool` is `peer` (flagship: residential and mobile, every country with stock), `mbl` (carrier modems in US, GB, FR, NL, PL, GE = Georgia; `mbl-de` always fails, use `peer-de`), `any` or `best`. `sticky` and `hard` pin the modem, not the IP; `sticky`/`auto*` need a `-sid-` to persist; unknown tokens are dropped silently (there is no `-session-` token). Limits: 500 concurrent connections and 250 sessions per account. ## Build and resell - [Build & resell](https://agents.proxies.sx/build/skill.md): mint per-customer `pak_` keys, set your own retail price, embed a dashboard or ship a storefront; kit https://github.com/bolivian-peru/proxy-reseller-kit, npm `@proxies-sx/pool-sdk` and `@proxies-sx/pool-portal-react` ## Bundles (one fetch) - [llms-full.txt](https://agents.proxies.sx/llms-full.txt): this file plus the modem skill, the quickstarts, the error table and the Pool Gateway skill, under 100 KB - Per-area bundles, each this file plus that area's skill: [peer (earn)](https://agents.proxies.sx/peer/llms-full.txt), [compute](https://agents.proxies.sx/compute/llms-full.txt), [build](https://agents.proxies.sx/build/llms-full.txt), [integrations](https://agents.proxies.sx/integrations/llms-full.txt), [dedicated port](https://agents.proxies.sx/marketplace/llms-full.txt), [private pool](https://agents.proxies.sx/private-pool/llms-full.txt), [tasks](https://agents.proxies.sx/tasks/llms-full.txt) ## Machine discovery - [x402 discovery](https://agents.proxies.sx/.well-known/x402.json): x402 services, wallets, pricing and management endpoints - [Agent card (A2A)](https://agents.proxies.sx/.well-known/agent-card.json): discovery-only card; act through the HTTP endpoints - [ERC-8004 registration](https://agents.proxies.sx/.well-known/erc8004-registration.json): on-chain agent registration record - [docs-manifest.json](https://agents.proxies.sx/docs-manifest.json): the numbers behind the docs - [Live status](https://api.proxies.sx/v1/x402/status): component health as JSON; human page https://agents.proxies.sx/status/ - [Sign up](https://client.proxies.sx/signup), [API keys](https://client.proxies.sx/account), [Buy GB](https://client.proxies.sx/purchase), [Private modems in the dashboard](https://client.proxies.sx/private-modems) ## Company - [About](https://agents.proxies.sx/about/), [Where our IPs come from](https://www.proxies.sx/where-our-ips-come-from), [Terms](https://www.proxies.sx/terms), [Privacy](https://www.proxies.sx/privacy) - [Contact](https://www.proxies.sx/contact): support and sales, maya@proxies.sx; [Telegram](https://t.me/proxies_sx); [X](https://x.com/sxproxies); [GitHub](https://github.com/bolivian-peru) ## Optional - [Earn: peer network](https://agents.proxies.sx/peer/skill.md): share bandwidth from consenting devices and get paid per GB; the live rate is `earningsPerGB` in the register response, never a hard-coded number. Go SDK 1.6.8 https://agents.proxies.sx/peer/go/ (Linux amd64 and arm64; Chinese steps https://agents.proxies.sx/peer/go/linux-zh.md), Node reference 1.6.6 https://agents.proxies.sx/peer/reference-sdk.js?v=1.6.6, Windows https://agents.proxies.sx/peer/windows/, Android https://github.com/bolivian-peru/android-peer-sdk (JitPack 1.3.2). Sign Partner Agreement 2.0 at https://farmer.proxies.sx/agreement first; devices are admitted only after approval (relay close 1013 until then). Payouts: https://farmer.proxies.sx/payouts. Supplier introductions carry no automatic commission. - [Mac compute](https://agents.proxies.sx/compute/skill.md): rent one approved Apple Silicon Mac for 30 days of inference (Starter 24 GB, Pro 48 GB, Max 96 GB; Qwen3.8 27B), or supply one. Early access, no SLA, no x402 checkout; purchases need `compute:purchase`, an `Idempotency-Key`, `expectedPriceUsd` and `pricingVersion`. Live tiers https://api.proxies.sx/v1/peer/compute/tiers, marketplace https://api.proxies.sx/v1/peer/compute/marketplace - [$SX token](https://agents.proxies.sx/sx-token/ecosystem.md): Solana meme token today; utility proposals in development; token ownership is separate from service access and supplier earnings. Overview https://agents.proxies.sx/sx-token/ - [Ground Truth](https://agents.proxies.sx/news/feed.md): source-backed research posts for agents (Atom: https://agents.proxies.sx/news/feed.xml). Latest: https://agents.proxies.sx/news/context-is-capacity/ - [Thesis](https://agents.proxies.sx/thesis/), [System map](https://agents.proxies.sx/system-map/), [Bot detection scanner](https://www.proxies.sx/tools/bot-detection-scanner), [Username builder](https://www.proxies.sx/tools/proxy-username-builder), [x402 SDK](https://github.com/bolivian-peru/x402-sdk) (`@proxies-sx/x402-core`, `-hono`, `-solana` 1.0.1; do not use 1.0.0), [@proxies-sx/lucid-agents](https://www.npmjs.com/package/@proxies-sx/lucid-agents), [Customer portal](https://client.proxies.sx) --- # Source: https://agents.proxies.sx/peer/skill.md # /peer-bandwidth - Proxies.sx Peer Network Skill > Buying proxies (not earning)? **Rotation cookbook** - copy-paste IP rotation + footguns: https://agents.proxies.sx/pool/rotation-cookbook.md > Canonical contract for the earn side of Proxies.sx. If any other file disagrees with this skill.md, this skill.md wins. If this skill.md disagrees with the live API, the live API wins. **WHAT:** Register a device, hold a WebSocket to a relay, and forward raw TCP bytes for customers. Earnings follow your active terms and eligible traffic routed through your device. **WHY:** Earn for eligible traffic your devices actually serve under your active partner terms. The configured model may use fixed rates or a share of collected revenue; there is no universal public percentage or guaranteed traffic. Read the live `earningsPerGB` registration response and your account earnings, and confirm payout method, minimum and timing with support. Do not infer your terms from an old example or another supplier’s agreement. **Before registering:** share bandwidth only from devices whose owners have given informed consent. Start with a small consented pilot and pass the self-test before expanding your fleet. Registration alone does not guarantee listing or earnings. Onboarding runs in this order: 1. **Account and key.** Create a farmer account at https://farmer.proxies.sx and an SDK API key (see "Supplier key" below). 2. **Agreement.** Sign Partner Agreement 2.0 at https://farmer.proxies.sx/agreement in your own legal name. 3. **Approval.** We review each signed agreement. Until it is approved the relay refuses your devices with WebSocket close `1013` "Exit identity temporarily unavailable". Nothing needs to change in your setup: keep the SDK running and the devices connect on a later retry after approval. If we ask for a correction, sign again; the devices stay refused until we approve the new signature. The Peers page of the farmer dashboard (https://farmer.proxies.sx/peers) shows the agreement state and, for each online device that is not on sale, the last test result and when the next test may run. 4. **One key, one device identity.** A device belongs to the API key that registered it. Switching that key off stops the device (the relay refuses it with the same `1013`), and registering with a different key creates a new device. 5. **Automatic test.** A device goes on sale only after it passes the automatic test through its tunnel, including the throughput floor returned as `throughputContract.minKBps` at registration (126 KiB/s on October 12, 2026). 6. **Refusals.** HTTP `429` at registration or on the relay WebSocket upgrade means wait for `Retry-After` and try again; it is not a rejection. `401 Invalid or inactive API key` and `403 Sign the current Peer Partner Agreement` do not clear by retrying: fix the key or sign the agreement first. **Supplier key:** open **Spawn API key** in the farmer header or https://farmer.proxies.sx/api-keys?purpose=sdk. Select **SDK / device**, name the key and choose **Create API key**. This preset grants `account:read`; use **Compute supplier** only for the separate Mac inference agent. Save the full secret privately before hiding it or leaving the page, then choose **Continue to SDK setup**. Review masked keys and revoke unused ones under **Existing keys** at https://farmer.proxies.sx/api-keys. There can be five active keys; do not revoke a key still used by a working fleet or replace device identity as a routine repair. A key does not bypass consent, agreement, quality or listing checks. **FIRST ACTION after these prerequisites** - register. Returns your device ID, JWT, refresh token, assigned relay, and the live rates: ```bash curl -s -X POST 'https://api.proxies.sx/v1/peer/agents/register' \ -H "Content-Type: application/json" \ -d '{"name":"my-agent-name","type":"claude","walletAddress":"So1anaWALLETxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","apiKey":"psx_your_api_key_here"}' ``` The wallet above is an obviously-fake placeholder. Use YOUR OWN Solana address - payouts go there and nowhere else. Include your own `apiKey` (a Proxies.sx `psx_...` key) to link the device to your account. Anonymous registration does not qualify the device for listed customer traffic. **THEN:** 1. Connect a WebSocket to the `relay` URL from the register response, JWT in the `Sec-WebSocket-Protocol` header as `token.` (Step 3). 2. Run the self-test once your device shows online (Step 4) - it catches the #1 implementation bug before it costs you weeks. 3. Implement the binary tunnel protocol (Step 5) - this is THE protocol; it is what carries paying traffic. 4. Check earnings (Step 6), withdraw to your registered wallet (Step 8). Don't want to implement the protocol yourself? Use a reference SDK - identical protocol, drop-in (see "Reference SDKs" below). **Support:** maya@proxies.sx or https://t.me/proxies_sx - quote any request/device id from the error. --- ## Required source declaration Complete https://farmer.proxies.sx/settings#peer-source: device owners/upstream suppliers, app and installation channels, actual country/network mix, informed permission before traffic, and withdrawal/revocation. Required answers can be in the supplier’s own language; keep them current. Submitted declarations are not independently verified KYC or consent. Identity/business verification is being prepared separately; never send identity documents through email/tickets or paste keys, user lists or exit IPs into the form. This collection rollout adds no automatic registration gate and does not rewrite signed agreements. ## Farmer account payments Open https://farmer.proxies.sx/payouts for outstanding earnings, amounts available to request, payment history and transaction receipts. Completed payments remain in history; settling a payment does not erase later earnings. A cash payment and the earnings it settles can differ, so do not infer the unpaid balance by subtracting cash receipts from lifetime earnings. Under https://farmer.proxies.sx/settings, native **SOL on Solana**, native **BTC on Bitcoin mainnet**, and **USDC on Solana** have separate payout settings. Check the currency and network before saving your own wallet. A SOL address does not mean a transfer is USDC. Bitcoin testnet addresses are not accepted. Saving a wallet or submitting a request does not send funds automatically. Manual farmer-account requests can include approved, unclaimed earnings of at least $0.01, even below the saved payout preference. Unapproved accrual is still visible as outstanding; changing the preference does not approve it. A completed native payment receipt displays the recorded asset amount, USD reference and explorer link when a transaction reference is available. Never request the same already-paid earnings again. These are the signed-in farmer account controls under `/v1/farmer/settings`, `/v1/farmer/earnings/summary`, `/v1/farmer/payouts/summary` and `/v1/farmer/payouts`. They do not change the separate agent/device withdrawal API thresholds documented below. Device SDK keys must not be used to manage private payout settings. Contact support for account-specific timing or a payment that is not yet reflected in history. ## Supplier referrals There is no automatic farmer-to-farmer referral commission program, personal supplier-referral link, or published percentage of another supplier's earnings. Earlier `PEER_` code and 10%-for-30-days examples described a proposed program, not active earning terms. Do not generate a referral link, add a referral field to SDK registration, or promise a commission from those examples. Supplier introductions are welcome: contact maya@proxies.sx with countries, network types and approximate distinct connections available online. Agree attribution, qualification, duration and any compensation in writing before proceeding on the basis of a referral fee. Each supplier uses their own farmer account and SDK key. The customer affiliate program at https://client.proxies.sx/affiliate applies to attributed proxy purchases, not another supplier's traffic earnings. ## Headless Linux fleet installation (Go 1.6.8) Linux ARM64: [package 1.6.8-arm64.1](https://agents.proxies.sx/peer/releases/1.6.8-arm64.1/proxies-peer-linux-arm64.tar.gz), [checksums](https://agents.proxies.sx/peer/releases/1.6.8-arm64.1/SHA256SUMS), [instructions](https://agents.proxies.sx/peer/go/#arm64). Built from unchanged Go SDK 1.6.8; `-version` reports 1.6.8. Requires 64-bit Linux aarch64/arm64, not ARMv7 or Android. Preserve the original key/name/state; this package does not establish server recovery. Go 1.6.8 retains the latest authenticated server-assigned relay in the existing fallback cycle and atomically saves accepted redirects in the existing identity file. Retries can return to an assigned spare after a temporary outage, including after an SDK restart. Existing manual relay pins, healthy sibling protection, stale-redirect checks, target safety, Retry-After and exponential backoff remain. One socket is the default; explicit WS_CONNECTIONS or -connections settings are preserved. Upgrade one stopped device first, keeping its key, name and state. This client change does not repair an overloaded relay or prove customer routing. Control traffic and health probes are not billable customer traffic. Download: https://agents.proxies.sx/peer/releases/1.6.8/proxies-peer-linux-amd64.tar.gz Checksums: https://agents.proxies.sx/peer/releases/1.6.8/SHA256SUMS English steps: https://agents.proxies.sx/peer/go/install.md Chinese steps: https://agents.proxies.sx/peer/go/linux-zh.md Source builds: use https://agents.proxies.sx/peer/releases/1.6.8/reference-sdk.go and the fresh-directory commands in the English/Chinese guide, including matching `go.mod`/`go.sum`, all three SHA-256 checks and the local `-version` check. Source SHA-256: `d18b26f4180fb73c9e1c7edcf8c764921132d967066d9e364824b1692a957ae6`. Matching source/module files and the package are immutable under `/peer/releases/1.6.8/`. Stop on a checksum mismatch. A download does not update an installed process; preserve the original key/name/state when upgrading one stopped device. Extract and verify the package, then run its local installer on a single Linux x86-64 device. Go is not required on the device; the service installer requires systemd 235+ and sudo. It reads the SDK key privately into a root-only 0600 environment file, creates a stable unique name once, runs unprivileged and persists state. Do not clone populated configuration/state. Use only consented devices with the appropriate current agreement. Go 1.6.8 checks the actual resolved IPv4 target before opening a TCP connection and refuses private, loopback, link-local, CGNAT, benchmark and multicast/reserved ranges. It keeps the existing public IPv4 routing policy; IPv6-only targets are not supported. Registration, saved identity and relay protocol are unchanged. This package does not automatically update devices or certify fleet capacity. Go 1.6.3 preserves connections and tunnels already on the redirect target and prevents competing redirects from both being accepted inside the existing 60-second anti-flap window. It retains the bounded target writes and 15-second deadline from 1.6.2. Keep the original key/name/state and upgrade one stopped device first using the installation guide; publication does not update running devices or attest fleet capacity. Go 1.6.1 adds startup REGISTER_JITTER_MS (default 30000), bounded registration retries with Retry-After, atomic checked state persistence and key/name binding. It reports its version with `-version`. The installer starts at one WebSocket per device; the standalone Go SDK also defaults to one as of 1.6.6; explicit settings are retained, normally capped at five, hard maximum six. Keep the original key/name/state when upgrading. Legacy named state migrates with the same name. Stop persistent 401/403 retry storms and review account/key/agreement status. Start with one device then small centrally paced cohorts. Require stable restart identity, token refresh/reconnect, actual country/type, listing, customer tunnel success, metering and resource headroom before expanding. Jitter and account limits are not capacity guarantees. At the September 25 audit, registration guards were 200,000 total / 20,000 unproven per registering key; do not create extra keys to bypass them. Several devices behind NAT may share one exit IP; the SDK does not bind processes to separate modems. Mobile-only supply requires verified cellular exits. The old “50 sockets per IP” and “online + verified means no test needed” instructions must not be used. ## Quick Reference | Action | Endpoint | Auth | |--------|----------|------| | Register | `POST /v1/peer/agents/register` | None (public, rate limited) | | Refresh Token | `POST /v1/peer/agents/{id}/refresh` | Refresh token | | Check Status | `GET /v1/peer/agents/{id}/status` | JWT required | | Check Earnings | `GET /v1/peer/agents/{id}/earnings` | JWT required | | Update Wallet | `PUT /v1/peer/agents/{id}/wallet` | JWT required | | Request Payout | `POST /v1/peer/agents/{id}/withdraw` | JWT required | | Connect Relay | `relay` URL from register response | JWT in header | | **Test Your Implementation** | `POST /v1/peer/my-devices/{id}/test` | Account API key / account JWT | | Toggle Listing | `PATCH /v1/peer/my-devices/{id}/listing` | Account API key / account JWT | | Check Verification | `GET /v1/peer/my-devices/{id}/verification` | Account API key / account JWT | | Get Auto-List Prefs | `GET /v1/peer/my-preferences` | Account API key / account JWT | | Update Auto-List | `PATCH /v1/peer/my-preferences` | Account API key / account JWT | | Bulk List Devices | `POST /v1/peer/my-devices/list-all` | Account API key / account JWT | | Remove a Device | `DELETE /v1/peer/my-devices/{id}` (stop the SDK first) | Account API key / account JWT | | Remove Stale Devices | `POST /v1/peer/my-devices/cleanup-stale` | Account API key / account JWT | **Two JWT worlds - do not mix them.** The peer registration JWT from Step 1 works on the `/v1/peer/agents/*` routes and the relay WebSocket ONLY. The `/v1/peer/my-devices/*` and `/v1/peer/my-preferences` routes take your ACCOUNT credentials: `X-API-Key: psx_...` (the key you registered the device with - recommended) or a client.proxies.sx account login JWT. The registration JWT returns 401 there. **Base URL:** `https://api.proxies.sx` --- ## Step 1: Register Your Agent Register as a bandwidth peer to get your JWT token, refresh token, and device ID. **Request:** ```http POST https://api.proxies.sx/v1/peer/agents/register Content-Type: application/json { "name": "my-agent-name", "type": "claude", "walletAddress": "So1anaWALLETxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "apiKey": "psx_your_api_key_here" } ``` **Parameters:** | Field | Type | Required | Constraints | |-------|------|----------|-------------| | `name` | string | Yes | 3-64 chars, alphanumeric + hyphens + underscores | | `type` | string | Yes | One of: `claude`, `gpt`, `custom` | | `walletAddress` | string | No | Valid Solana address (32-44 base58 chars). YOUR wallet - the example above is a fake placeholder | | `apiKey` | string | For listed traffic | Your Proxies.sx API key (`psx_...`) - links the device to the account responsible for its consent and management | **Response - persist `deviceId`, `jwt`, `refreshToken`, `relay`:** ```json { "deviceId": "agent_abc123def456", "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refreshToken": "a1b2c3d4e5f6...", "relay": "wss://relay.proxies.sx", "earningsPerGB": { "mobile": , "residential": , "datacenter": }, "throughputContract": { "minKBps": 126, "probeUrl": "https://speed.cloudflare.com/__down?bytes=262144", "probeIntervalMin": 5 }, "instructions": "Connect via WebSocket with Sec-WebSocket-Protocol header. JWT expires in 1 hour - use refresh token to get new JWT." } ``` **IMPORTANT:** - Save both the `jwt` AND `refreshToken` securely - JWT expires in **1 hour** - Use the refresh token to get a new JWT without re-registering - `earningsPerGB` in this response is the live rate at this moment - it is the only place rates are authoritative - `throughputContract.minKBps` is the customer-routable floor in KiB/s (1024 bytes per second per unit): your device must sustain at least that probed throughput, plus the other eligibility checks, to receive customer traffic; use the returned value rather than hardcoding this example (see "Speed Tiers" below) - **Connect to the `relay` URL from THIS response - do not hardcode `wss://relay.proxies.sx`.** The platform runs multiple regional relays and geo-assigns you the nearest one (e.g. US/LATAM peers get `wss://relay-us.proxies.sx`). Using the assigned relay is what gives you full throughput; a far relay caps a single TCP stream by latency. **Rate Limit:** up to 600 registrations per minute per source IP, with burst limits and shared capacity protection. This is a ceiling, not guaranteed throughput. A shared limit can return `429` even when your own request rate is low. **Getting `429` on register?** It means "slow down", not "fail" - handle it, do not crash-restart (a fast restart loop only sustains the 429). Do all three: 1. **Back off with jitter** on 429 and retry in-process (respect `Retry-After`). The Node reference v1.6.6+ and Go v1.6.1+ do this for you; older/hand-written clients must add it. 2. **Persist identity** (`deviceId` + `refreshToken`) and REFRESH instead of re-registering on restart - re-registering every restart is the usual 429 cause. 3. **Stagger** a fleet's first registration (the reference SDK's `REGISTER_JITTER_MS` spreads it automatically; default ~20s). If `429` persists at a low coordinated fleet rate, contact support with one device name and the exact UTC time. We can check shared capacity and arrange a bounded onboarding window. Do not send an API key. Registration capacity, device quality checks and paid-availability terms are separate; registration alone guarantees neither listing nor earnings. --- ## Step 2: Refresh Your Token (when JWT expires) JWT tokens expire in 1 hour. Use your refresh token to get a new one. **Request:** ```http POST https://api.proxies.sx/v1/peer/agents/{deviceId}/refresh Content-Type: application/json { "refreshToken": "a1b2c3d4e5f6..." } ``` **Response:** ```json { "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresIn": "1h" } ``` **Note:** Refresh tokens expire in 7 days. If expired, re-register your agent. --- ## Step 3: Connect to the Relay Open a WebSocket connection to start routing traffic. **SECURITY:** The JWT goes in the `Sec-WebSocket-Protocol` header as `token.` - never in the URL. URL-based tokens leak into logs and cause intermittent auth failures. **Connection:** ```javascript // Connect to the relay from your register response (geo-assigned), NOT a // hardcoded host. `activeRelay` starts as register.relay and can be updated // at runtime by a `relay_redirect` (see below). let activeRelay = register.relay || 'wss://relay.proxies.sx'; let heartbeatTimer = null; // Sec-WebSocket-Protocol header (RECOMMENDED) const ws = new WebSocket(activeRelay, [`token.${YOUR_JWT}`]); ws.onopen = () => { console.log('Connected to relay server'); // REQUIRED: send a heartbeat every 30s - the relay replies heartbeat_ack. // The relay never pings you; peers silent for 120s (4 missed beats) are // force-closed by the zombie sweep and marked offline. heartbeatTimer = setInterval(() => { ws.send(JSON.stringify({ type: 'heartbeat', payload: {} })); }, 30000); // Send device info (optional but recommended for targeting) ws.send(JSON.stringify({ type: 'device_info', payload: { country: 'US', carrier: 'T-Mobile', currentIp: '174.205.x.x', // For logging only, server classifies IP protocol: 'binary-v1', // opt into binary tunnel_data (faster) supportsRelayRedirect: true // honor server-driven nearest-relay routing } })); }; ws.onmessage = (event) => { const message = JSON.parse(event.data); handleMessage(message); }; ws.onerror = (error) => { console.error('WebSocket error:', error); }; ws.onclose = (event) => { console.log('Disconnected from relay', event.code, event.reason); if (heartbeatTimer) { clearInterval(heartbeatTimer); heartbeatTimer = null; } // Implement reconnection logic // If code 4001/4002, refresh your JWT ONCE for all sockets first }; ``` Send `{ "type": "heartbeat" }` every 30 seconds - the relay replies with `heartbeat_ack`. The relay never initiates heartbeats; peers that stop sending them for 120 seconds are force-closed by the zombie sweep and marked offline. **Close Codes:** | Code | Meaning | Action | |------|---------|--------| | 4001 | Token required | You sent no token - include the JWT as `Sec-WebSocket-Protocol: token.` | | 4002 | Invalid token | JWT invalid/expired - refresh your JWT once for ALL sockets, then reconnect | | 4008 | Pool cap exceeded | Device already holds 6 sockets - the oldest is evicted, the new socket stays | | 4029 | Rate limit | Too many messages, slow down | | 1013 | Backend transiently unavailable | Retry with the SAME token - do NOT re-register | | 1013 "Exit identity temporarily unavailable" | The relay will not admit this device now: the agreement is not approved yet, the registering API key is switched off, or the previous session is still closing | Keep retrying with the SAME token; if it persists, check the agreement state on the dashboard Peers page and that the key is active. Do NOT re-register | **Connection Limits:** Up to 6 simultaneous WebSocket connections per device (multi-WS pool for throughput; opening a 7th evicts the oldest with close code 4008). The reference SDK defaults to one socket; explicit counts are retained. **Message Rate Limit:** 100 messages per minute per device. Connection closed if exceeded. --- ## Step 4: Test Your Implementation (do this before scaling up) **Run this before registering hundreds or thousands of devices.** The most common integration bug is subtle: your client accepts the `CONNECT` request and completes a fast handshake, but then never forwards the raw TLS bytes between the customer and the target site - it looks connected, it looks healthy, but it earns $0 forever because it never actually carries traffic. This has stranded entire fleets of devices for weeks before anyone noticed. Once your device shows up as `online`, run a real end-to-end test against it (the exact same CONNECT + TLS + HTTP request a paying customer would make): **Request:** ```http POST https://api.proxies.sx/v1/peer/my-devices/{deviceId}/test X-API-Key: psx_your_api_key_here ``` **Auth note:** this endpoint takes your ACCOUNT credentials, not the peer registration JWT from Step 1 (the registration JWT returns 401 here). Use the `X-API-Key: psx_...` you registered the device with (recommended), or a client.proxies.sx account login JWT as `Authorization: Bearer `. Devices registered without an `apiKey` have no self-test path - register with one. **Response (pass):** ```json { "passed": true, "diagnosis": { "passed": true, "reasonCode": null, "summary": "Your device completed a real CONNECT + TLS + HTTP request through the gateway, exactly as a paying customer would.", "fix": null } } ``` **Response (the most common failure - fix this before scaling):** ```json { "passed": false, "diagnosis": { "passed": false, "reasonCode": "tls_dropped_bernard_pattern", "summary": "Your device accepted the CONNECT request and completed a fast handshake, but the TLS bytes were never forwarded - the tunnel dropped mid-stream. This is the single most common bug in custom clients: something on your side is terminating or re-framing the TLS connection instead of passing raw bytes through untouched.", "fix": "Do not call tls.connect() (or your language's TLS wrapper) on the tunnel after CONNECT - just relay raw bytes both directions with zero modification. See the correct passthrough pattern in our reference client, or switch to it directly: API_KEY=psx_... node reference-sdk.js" } } ``` `diagnosis.fix` (when present) always points at the concrete next step. Rate limit: 5 tests/minute per device. Re-run this test after every protocol change you make. Passing once, then breaking silently, is how devices end up online-but-earning-nothing. --- ## Step 5: The Tunnel Protocol (this is what carries paying traffic) The relay uses a streaming TCP tunnel for **both HTTP and HTTPS** customer traffic. Control messages are JSON; the data hot path should be binary. Advertise binary support in `device_info`: ```json { "type": "device_info", "payload": { "protocol": "binary-v1" } } ``` With `binary-v1`, the relay sends tunnel data as binary WebSocket frames (no base64, no JSON envelope per chunk). Without it, the relay falls back to legacy JSON+base64 tunnel data (slower, still supported). Either way, the lifecycle is: 1. Relay sends JSON `tunnel_connect` with `{sessionId, host, port}` 2. You open a TCP socket to `host:port` and reply JSON `tunnel_connected` 3. Bytes flow both directions - raw, untouched, immediately (binary frames if you advertised `binary-v1`) 4. Either side closes; you send `tunnel_closed` ### Incoming - tunnel_connect (JSON, always) ```json { "type": "tunnel_connect", "payload": { "sessionId": "uuid-session-123", "host": "example.com", "port": 443 } } ``` ### Handler ```javascript // Open TCP connection to target const sock = net.connect(port, host); tunnels.set(sessionId, sock); sock.on('connect', () => { ws.send(JSON.stringify({ type: 'tunnel_connected', payload: { sessionId } })); }); sock.on('data', (data) => { // binary-v1 hot path (preferred): ws.send(encodeBinary(MSG_TUNNEL_DATA, sessionId, data)); // legacy JSON fallback (only if you did NOT advertise binary-v1): // ws.send(JSON.stringify({ type: 'tunnel_data', payload: { sessionId, data: data.toString('base64') } })); }); sock.on('close', () => { tunnels.delete(sessionId); ws.send(JSON.stringify({ type: 'tunnel_closed', payload: { sessionId } })); }); ``` ### Binary frame format (hot path, v1) ``` byte 0 : message type - 0x01 = tunnel_data, 0x03 = tunnel_close byte 1 : sessionId length (1 byte, max 255) bytes 2..N : sessionId as UTF-8 bytes N+1+ : raw payload (NOT base64) ``` ### Encoding ```javascript const MSG_TUNNEL_DATA = 0x01, MSG_TUNNEL_CLOSE = 0x03; function encodeBinary(type, sessionId, data) { const sid = Buffer.from(sessionId, 'utf-8'); const hdr = Buffer.from([type, sid.length]); return Buffer.concat([hdr, sid, data || Buffer.alloc(0)]); } function decodeBinary(buf) { const sidLen = buf[1]; return { type: buf[0], sessionId: buf.slice(2, 2 + sidLen).toString('utf-8'), payload: buf.slice(2 + sidLen) }; } // Send bytes from target socket -> relay: ws.send(encodeBinary(MSG_TUNNEL_DATA, sessionId, chunkBuffer)); ``` ### Message handler (both binary + JSON) ```javascript ws.on('message', (raw, isBinary) => { if (isBinary && raw.length > 0 && raw[0] !== 0x7B) { const dec = decodeBinary(raw); if (dec.type === MSG_TUNNEL_DATA) { var s = tunnels.get(dec.sessionId); if (s) s.write(dec.payload); } if (dec.type === MSG_TUNNEL_CLOSE) { var s2 = tunnels.get(dec.sessionId); if (s2) s2.destroy(); } return; } const msg = JSON.parse(raw.toString()); // ...JSON control messages (tunnel_connect, tunnel_close, heartbeat_ack) }); ``` ### Legacy JSON data path (still supported, slower) If you did not advertise `binary-v1`, tunnel data arrives and departs as JSON+base64: **Incoming - tunnel_data (relay sends data to forward):** ```json { "type": "tunnel_data", "payload": { "sessionId": "uuid-session-123", "data": "base64-encoded-tls-data" } } ``` Write it to the tunnel socket: `tunnels.get(sessionId).write(Buffer.from(data, 'base64'))` **Incoming - tunnel_close:** ```json { "type": "tunnel_close", "payload": { "sessionId": "uuid-session-123" } } ``` Destroy the socket: `tunnels.get(sessionId).destroy()` ### Reference SDKs (drop-in, all speak the identical protocol) - **Node.js source (1.6.6)**: https://agents.proxies.sx/peer/reference-sdk.js?v=1.6.6 -> `API_KEY=psx_... node reference-sdk.js` - **Go / headless Linux x86-64 (1.6.8)**: https://agents.proxies.sx/peer/go/ — standalone package, checksums and protected-key systemd installer; no GUI or compiler on the device. Chinese guide: https://agents.proxies.sx/peer/go/linux-zh.md. Source: https://agents.proxies.sx/peer/releases/1.6.8/reference-sdk.go with matching module files and checksum/version checks in the guide. - **Windows** (Node app, double-click): https://agents.proxies.sx/peer/windows/releases/1.6.6/proxies-peer-windows-node-1.6.6.zip - **Android (JitPack 1.3.2)**: https://github.com/bolivian-peru/android-peer-sdk — exact tag and downloadable POM/AAR verified; the GitHub latest-release badge may lag. Don't hand-roll unless you must. Porting to a new language? The two mistakes that cause "intermittent auth" and "zero traffic": (1) put the JWT in the `Sec-WebSocket-Protocol` header as `token.`, never the URL; (2) on a 4001/4002 close, refresh the identity ONCE for all sockets - never relaunch your whole socket pool (that leaks connections and dials with stale tokens). The Go and Node files both document this inline. **SDK implementer checklist:** - Pump bytes from your TCP socket to the WS as they arrive - don't batch-flush every N seconds (that's the lws-wakeup bug) - Send the first byte within ~200ms of `tunnel_connect` if you can - Match the relay's `setNoDelay(true)` on your outbound socket - Advertise `protocol: "binary-v1"` in `device_info` for ~33% smaller frames and lower CPU - Send a `heartbeat` every 30s (peers silent for 120s are killed via the zombie sweep) --- ## Step 6: Check Your Earnings Monitor your earnings and traffic stats. **Request:** ```http GET https://api.proxies.sx/v1/peer/agents/{deviceId}/earnings Authorization: Bearer YOUR_JWT ``` **Response:** ```json { "totalEarnedCents": 2500, "pendingPayoutCents": 1500, "totalPaidOutCents": 1000, "totalTrafficMB": 10240, "totalTrafficGB": 10.0, "canRequestPayout": true, "minimumPayoutCents": 500, "walletAddress": "So1anaWALLETxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "ipType": "mobile", "earningsPerGB": { "mobile": , "residential": , "datacenter": } } ``` **Key Fields:** - `pendingPayoutCents`: Amount available to withdraw (in cents) - `canRequestPayout`: `true` if pending >= minimum ($5.00 agents / $10.00 SDK) - `ipType`: Your detected IP type (determines your earnings rate) --- ## Step 7: Update Wallet Address Update your payout wallet address. **Note:** This triggers a 7-day security cooling period. **Request:** ```http PUT https://api.proxies.sx/v1/peer/agents/{deviceId}/wallet Authorization: Bearer YOUR_JWT Content-Type: application/json { "walletAddress": "NEW_SOLANA_ADDRESS_HERE" } ``` **Response:** ```json { "success": true, "message": "Wallet updated. 7-day cooling period before payouts allowed.", "coolingOffUntil": "2026-02-09T12:00:00.000Z" } ``` **Security:** - Rate limit: 1 wallet change per 24 hours - 7-day cooling period before payouts after wallet change - Withdrawals always use your registered wallet (cannot be changed at withdrawal time) --- ## Step 8: Request Payout Withdraw your earnings to your registered Solana wallet. **Request:** ```http POST https://api.proxies.sx/v1/peer/agents/{deviceId}/withdraw Authorization: Bearer YOUR_JWT Content-Type: application/json { "walletAddress": "So1anaWALLETxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } ``` **Response:** ```json { "success": true, "message": "Payout request submitted. Admin will process within 24-48 hours.", "requestedAmountCents": 1500, "walletAddress": "So1anaWALLETxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } ``` **Requirements:** - Minimum payout: $5.00 (500 cents) for AI agents | $10.00 (1000 cents) for Android SDK devices - Valid Solana address (32-44 base58 characters) - Rate limit: 3 withdrawal requests per hour - **Security:** Payout goes to your registered wallet ONLY - the `walletAddress` body parameter is ignored if you already have one registered. Do not build tooling that assumes per-call payout redirection; it does not exist. --- ## The Goal: HTTP Endpoint From Great IPs Everything above is the standard path. But the core goal is simple: **get us an HTTP proxy endpoint from a device with a valuable IP address.** The WebSocket relay is one path. The Android SDK is another. Docker is a third. But if you find a better way to expose an HTTP proxy from a mobile phone, a residential router, a 5G hotspot, a Raspberry Pi, an IoT fleet, or any device with a real-world IP - **do it your way.** Build a custom SOCKS5-to-HTTP bridge, run a mesh network, write firmware for a router. If it delivers an HTTP endpoint from a real IP, we want it. The only requirements: the IP must be real (server-side ASN classification determines type and rate), traffic must flow through your device, and you connect via the relay protocol so we can route and track. Beyond that - be creative, code your own solution, invent your own method. If something is unclear, don't wait for documentation. Build it and ship it. --- ## IP Classification & Earnings ### Earnings by IP Type | IP Type | Earnings/GB | Examples | |---------|-------------|----------| | **Mobile** | **Highest tier** | AT&T, Verizon, T-Mobile, Vodafone | | **Residential** | **Mid tier** | Comcast, Spectrum, Cox, BT | | **Datacenter** | **Base tier** | AWS, GCP, Azure, Hetzner, VPNs | Actual per-GB numbers: read `earningsPerGB` in your register or earnings response. Rates are demand-based and change; no static number in any document is authoritative. ### How Detection Works (Server-Side) 1. When you connect, we look up YOUR IP's ASN (Autonomous System Number) 2. ASN is checked against 100+ datacenter provider ASNs 3. ASN is checked against 200+ mobile carrier ASNs 4. Classification: `mobile` | `residential` | `datacenter` | `unknown` 5. **IMPORTANT:** We classify YOUR connection IP, not what you report in `device_info` **Security Note:** IP type is determined server-side via ASN lookup. Device-reported `ipType` in `device_info` messages is ignored for earnings calculation. This prevents spoofing. --- ## WebSocket Message Types ### Outgoing (Agent -> Relay) | Type | Description | |------|-------------| | `device_info` | Send device metadata (country, carrier - for targeting, not earnings) | | `tunnel_connected` | TCP tunnel established to target host | | `tunnel_data` | Forward data from target back to relay (binary frame preferred) | | `tunnel_closed` | TCP tunnel closed | | `heartbeat` | Keepalive YOU must send every 30s - miss for 120s and the relay closes your socket | | `proxy_response` | DEPRECATED - response in the legacy buffered flow (see Appendix) | | `proxy_error` | DEPRECATED - error in the legacy buffered flow (see Appendix) | ### Incoming (Relay -> Agent) | Type | Description | |------|-------------| | `connected` | Connection established, includes deviceId | | `tunnel_connect` | Open TCP tunnel to target host:port | | `tunnel_data` | Forward data from relay to target socket | | `tunnel_close` | Close TCP tunnel | | `heartbeat_ack` | Relay ack of your heartbeat - no action needed | | `relay_redirect` | Reconnect to a nearer relay (see below). Only sent if you advertised `supportsRelayRedirect: true` | | `proxy_request` | DEPRECATED - legacy buffered HTTP request (see Appendix) | --- ## Multi-Region Relay Routing (`relay_redirect`) The platform runs regional relays and routes each peer to the nearest one for maximum throughput. Routing is fully server-controlled: 1. **At registration** you get the geo-assigned relay in `register.relay`. Connect there. 2. **At runtime**, whichever relay you land on may send a `relay_redirect` telling you a nearer one exists for your IP's location. This is how the fleet migrates itself when a new region comes online - no re-install needed. **Message:** ```json { "type": "relay_redirect", "payload": { "relay": "wss://relay-us.proxies.sx", "reason": "geo" } } ``` **How to honor it (required for `supportsRelayRedirect: true` peers):** ```javascript case 'relay_redirect': { const target = msg.payload && msg.payload.relay; // Only honor *.proxies.sx wss URLs; ignore if you have an explicit relay pin. if (!/^wss:\/\/[a-z0-9.-]+\.proxies\.sx(?:\/|$)/i.test(target)) break; if (target === activeRelay) break; if (Date.now() - lastRedirectAt < 60000) break; // 60s anti-flap guard lastRedirectAt = Date.now(); activeRelay = target; ws.close(4100, 'relay_redirect'); // your close handler reconnects to activeRelay break; } ``` **Rules:** - Validate the target is a `*.proxies.sx` wss URL before honoring (defense in depth). - Apply a 60s anti-flap guard so a flapping geo-classification can't ping-pong you. - An explicit operator relay override (e.g. a `RELAY_URL` env) should win - ignore redirects when set. - If you DON'T advertise `supportsRelayRedirect`, you'll never be redirected - but you'll stay on whatever relay you first connected to, which may be far and slow. --- ## Tunnel Lifecycle - What Kills Your Tunnel The relay enforces three independent watchdogs to protect customers from broken peer SDKs. If your agent gets demoted (`workingForCustomers: false`), one of these probably fired. Implement your byte-pump correctly to avoid: | Defense | Triggers when | Effect | |---|---|---| | **Open watchdog** (5s) | After `tunnel_connect`, you must send the first `tunnel_data` (or binary v1 frame) within 5 seconds. The relay writes "200 Connection Established" to the customer optimistically - if no bytes ever flow back, the customer hangs | Relay force-closes, POSTs to backend `/v1/peer/internal/tunnel-failure` with `reason: tunnel_open_timeout`. 3 such failures = `listedForSale: false` | | **Mid-stream stall** (30s) | Once first bytes have flowed, no further bytes in either direction for 30 seconds while the tunnel is still open | Force-close, `reason: tunnel_stall_idle`. Same demote semantics | | **Backpressure cap** (16MB sustained 10s) | The customer's downlink is slower than your agent is pushing data; the write buffer toward the customer stays above 16MB for more than 10 seconds | Force-close. Your agent isn't penalized - this is a customer-side problem | The **most common failure mode (Bernard pattern)** is: agent accepts `tunnel_connect`, opens TCP to (host, port), but the byte-bridge from target -> customer never runs. Relay's open-watchdog fires at 5s, customer sees `SSL_ERROR_SYSCALL` mid-TLS. Backend's CONNECT-mode probe catches this explicitly and logs `lastFailureReason: tls_dropped_bernard_pattern`. The self-test in Step 4 reproduces exactly this check on demand - run it. --- ## Speed Tiers & Capacity Caps The backend's CONNECT-mode probe measures TLS-handshake-ms and throughput-KB/s on every probe cron tick (every 5 min, batched, most-overdue first; a separate throughput sweep runs every 30 min). The result is a speed tier that drives how many concurrent connections your endpoint serves: | Tier | Thresholds | maxConnections | maxCustomers | |---|---|---|---| | fast | throughput >= 300 KB/s AND TTFB <= 1500ms | 4 | 1 | | medium | throughput >= 80 KB/s AND TTFB <= 4000ms | 3 | 1 | | slow | below medium | 2 | 1 | | unknown | never been probed (just registered) | 2 | 1 | Peers are exclusive: one customer session per peer device (the customer gets a clean, unshared exit IP). Faster peers earn more - the one customer can open more parallel connections through them and the selector prefers them. The probe is adaptive: a tunnel-failure event triggers a single-peer re-probe with 1-3s jitter (60s per-device cooldown) so good behavior recovers fast. **Independent of speed tier**, a peer is only CUSTOMER-ROUTABLE when its probed throughput meets the `minKBps` value returned in `throughputContract.minKBps` at registration. This is a configurable platform policy, not an SDK-version constant. Below that floor the device may stay online, but online status and diagnostic probe traffic do not guarantee earnings. Usage earnings require billable customer traffic under the supplier agreement. The routing selector excludes peers below the floor from customer traffic. --- ## Security Summary | Feature | Value | |---------|-------| | JWT expiry | **1 hour** | | Refresh token expiry | 7 days | | Max WebSockets per device | 6 (over-cap evicts oldest, close 4008) | | Message rate limit | 100/min | | Registration rate limit | 600/min (agents), 5/min/IP (SDK) | | Wallet change rate limit | 1/day | | Wallet cooling period | 7 days | | Withdrawal rate limit | 3/hour | | IP classification | Server-side (ASN lookup) | | Token revocation | DB-level check on every request | | SDK endpoints | All authenticated (JWT required) | --- ## Listing Your Device in the Pool Gateway Once your device is connected and earning, you can list it for sale in the Pool Gateway - making it available to customers who purchase proxy bandwidth. ### How It Works 1. **Connect your device** - Register and connect via WebSocket (Steps 1-5 above) 2. **Toggle "Listed for Sale"** - Via farmer dashboard (farmer.proxies.sx/peers) or API 3. **Automated verification** - System checks IP quality, speed, ISP legitimacy, VPN/proxy detection 4. **Quality score** - Device gets a 0-100 score based on checks 5. **Approval** - Auto-approved by the rule under "Why a device is awaiting verification" below, or admin-approved manually 6. **Live in pool** - Verified devices appear in the gateway and serve customer traffic ### Verification Checks | Check | What It Verifies | |-------|-----------------| | IP Classification | Residential or mobile to be listed as such; hosting and datacenter networks are not put on sale at the moment and are never listed as residential or mobile; VPN is rejected | | ISP/ASN Validation | ASN checked against known datacenter and VPN providers | | VPN/Proxy Detection | ISP name scanned for VPN indicators (NordVPN, Mullvad, etc.) | | GeoIP Match | Server-classified country must match claimed country | | Uptime | Online for about 2 minutes before the check | | Fraud Flags | Device must not be flagged for anomalies | ### Listing API | Action | Endpoint | Auth | |--------|----------|------| | Toggle listing (single device) | `PATCH /v1/peer/my-devices/{deviceId}/listing` | Account API key / account JWT | | Check verification | `GET /v1/peer/my-devices/{deviceId}/verification` | Account API key / account JWT | | Get auto-list preferences | `GET /v1/peer/my-preferences` | Account API key / account JWT | | Enable/disable auto-list | `PATCH /v1/peer/my-preferences` | Account API key / account JWT | | Bulk list all eligible devices | `POST /v1/peer/my-devices/list-all` | Account API key / account JWT | | Remove a device (stop the SDK first) | `DELETE /v1/peer/my-devices/{deviceId}` | Account API key / account JWT | | Remove stale devices | `POST /v1/peer/my-devices/cleanup-stale` | Account API key / account JWT | **Auth:** these routes take ACCOUNT credentials - `X-API-Key: psx_...` (recommended) or a client.proxies.sx account login JWT. The peer registration JWT from Step 1 does NOT work here. ### Auto-List (Recommended for Fleets) Running many devices? Enable auto-listing once and every future device registered with your API key is listed automatically - no manual toggle per device. ```http PATCH https://api.proxies.sx/v1/peer/my-preferences X-API-Key: psx_your_api_key_here Content-Type: application/json { "autoListDevices": true } ``` Already have many devices that weren't auto-listed? Run a one-shot bulk: ```http POST https://api.proxies.sx/v1/peer/my-devices/list-all X-API-Key: psx_your_api_key_here ``` Lists every online, eligible (mobile/residential, payable IP) device in a single call. **Toggle listing:** ```http PATCH https://api.proxies.sx/v1/peer/my-devices/{deviceId}/listing X-API-Key: psx_your_api_key_here Content-Type: application/json { "listedForSale": true } ``` **Requirements to list:** - Device must be online - IP type must be residential or mobile (datacenter rejected) - Device must be payable (isPayable = true) ### Verification Statuses | Status | Meaning | |--------|---------| | **Pending** | Listed but not yet verified - auto-check runs every 5 minutes | | **Verified** | Passed all checks - live in gateway pool, serving customer traffic | | **Rejected** | Failed checks - reason provided, fix the issue and re-list | ### Anti-Fraud Protection - Server-side IP classification (device-reported type ignored) - VPN/proxy/Tor detection via ISP name and ASN database - Hosting/datacenter IP not put on sale at the moment, and never listed as residential or mobile - Anomaly detection flags devices with >$1000 earnings or >100GB/hour - Re-verification every hour for auto-verified devices - Admin-approved devices are protected from automated re-checks - Offline devices auto-unlisted after 1 hour --- ## Stop Sharing or Remove a Device Your device is yours. You can stop or leave at any time, without asking us and without a notice period. Earnings already recorded stay on your account and are paid as usual, including for a device you deleted. **Pause.** Stop the SDK process. The relay marks the device offline and no traffic is routed to it. Start the process again and the same identity resumes. **Delist without stopping.** `PATCH /v1/peer/my-devices/{deviceId}/listing` with `{"listedForSale": false}` (or the Listed switch in the farmer dashboard). The device stays connected and verified but carries no customer traffic; automation never lists it again. `{"listedForSale": true}` lists it again: at once if verified, otherwise as soon as verification passes. `POST /v1/peer/my-devices/list-all` does the same for every device. **Clean up after a test run.** `POST /v1/peer/my-devices/cleanup-stale` with `{"olderThanDays": 1}` deletes devices offline for more than a day; `{"olderThanDays": 0}` deletes every device offline right now. Online devices are never touched. A deleted device comes back if its SDK starts again with the same saved identity. **Why a device is "awaiting verification".** Only online devices are tested; tests run continuously and devices take turns, so there is no fixed completion time. A device is verified automatically in one of two ways: it passes the connection quality check (network type, VPN and hosting detection, country), or it has at least 5 routing tests with at least 70% passing and the latest one passing. Mobile and residential connections are listed as such. Hosted and datacenter connections are not put on sale at the moment, and are never listed as residential or mobile. To go on sale it must also meet the routing throughput floor in `throughputContract.minKBps`; country, classification, latency, quality, consent and account gates still apply. Numeric registration examples are illustrative, not a fixed threshold. Online + verified does not prove listed/routable or earning: test a small cohort through customer routing, reconnect/token refresh and metering before scaling. Never lower classification or consent gates merely to grow counts. **Remove a device for good. The order matters: stop first, then delete.** 1. Stop the SDK on that device and make sure it does not start again (remove your service, cron entry or app). A device that reports online again is restored automatically, so a delete while the SDK still runs does not last. 2. Delete the saved identity so a later run cannot resume it: `~/.proxies-peer-state.json` for the Node and Go reference agents (or the path in `PEER_STATE_FILE` / `-state`). 3. Delete the device on Proxies.sx, in the farmer dashboard or with your account API key: ``` DELETE https://api.proxies.sx/v1/peer/my-devices/{deviceId} X-API-Key: psx_your_account_key 200 {"deleted": true, "deviceId": "...", "remaining": 12} 404 not your device, or already removed ``` It is removed from every list, from your stats and from the routing pool at once. 4. If no other device uses the API key, revoke it under API keys in the farmer portal. A key cannot revoke itself. **Many dead duplicates?** One call removes every device of yours that has been offline and inactive for at least `olderThanDays` (default 1): ``` POST https://api.proxies.sx/v1/peer/my-devices/cleanup-stale X-API-Key: psx_your_account_key {"olderThanDays": 1} 200 {"deleted": 214, "remaining": 12, "olderThanDays": 1} ``` Apple Silicon compute nodes have their own one-command removal: https://agents.proxies.sx/compute/#leave ## Earnings Summary | Metric | Value | |--------|-------| | Per-GB rates | Dynamic, set by platform - read `earningsPerGB` in the register or earnings response | | Minimum payout | $5.00 (agents) / $10.00 (Android SDK devices) | | Payment currency | USDC | | Payment network | Solana | | Processing time | 24-48 hours | --- ## Quick Test (curl) Register with a wallet and API key so the device is payable and manageable from the start - replace both placeholders with your own values: ```bash # 1. Register (replace the placeholder wallet with YOUR Solana address) RESPONSE=$(curl -s -X POST 'https://api.proxies.sx/v1/peer/agents/register' \ -H "Content-Type: application/json" \ -d '{"name":"my-test-agent","type":"claude","walletAddress":"So1anaWALLETxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","apiKey":"psx_your_api_key_here"}') echo "$RESPONSE" | jq # Extract tokens DEVICE_ID=$(echo "$RESPONSE" | jq -r '.deviceId') JWT=$(echo "$RESPONSE" | jq -r '.jwt') REFRESH_TOKEN=$(echo "$RESPONSE" | jq -r '.refreshToken') # 2. Check earnings curl -s "https://api.proxies.sx/v1/peer/agents/${DEVICE_ID}/earnings" \ -H "Authorization: Bearer $JWT" | jq # 3. Refresh token when JWT expires (after 1 hour) curl -s -X POST "https://api.proxies.sx/v1/peer/agents/${DEVICE_ID}/refresh" \ -H "Content-Type: application/json" \ -d "{\"refreshToken\":\"$REFRESH_TOKEN\"}" | jq ``` --- ## Links | Resource | URL | |----------|-----| | **Agents Landing** | https://agents.proxies.sx | | Peer Network | https://agents.proxies.sx/peer/ | | Buy Proxies (Dedicated Port) | https://agents.proxies.sx/marketplace/ | | This Skill File | https://agents.proxies.sx/peer/skill.md | | Master Skill File | https://agents.proxies.sx/skill.md | | Pool Gateway Access Skill | https://agents.proxies.sx/pool/skill.md | | LLMs.txt | https://agents.proxies.sx/llms.txt | | Ecosystem Master Doc | https://agents.proxies.sx/sx-token/ecosystem.md | | **API Docs (Pool Gateway Swagger, public)** | https://api.proxies.sx/docs/gateway | | API Docs (Reseller OpenAPI JSON, public) | https://api.proxies.sx/v1/reseller/docs/openapi | | API Docs (full customer Swagger - basic-auth gated, NOT public) | https://api.proxies.sx/docs/api | | Relay Server | wss://relay.proxies.sx | | MCP Server (proxy) | `npx -y @proxies-sx/mcp-server` | **Support:** maya@proxies.sx or https://t.me/proxies_sx. --- ## Proxies.sx Ecosystem This Peer Network (earn side) is one product in the Proxies.sx ecosystem: ### Products | Product | URL | Description | |---------|-----|-------------| | Peer Network | https://agents.proxies.sx/peer/ | Earn USDC by sharing bandwidth (this file) | | Pool Gateway | https://agents.proxies.sx/pool/skill.md | Use the network you supply - one credential, every country (standard: API key + deposited GB; x402 optional) | | Dedicated Port | https://agents.proxies.sx/marketplace/skill.md | Buy a dedicated mobile proxy (standard: API key + deposited GB; x402 optional) | | Customer Dashboard | https://client.proxies.sx | Proxy management portal | ### Buying proxies (separate product surface) This file is the earn-side contract only. To BUY proxies, read the buy-side contracts; do not infer them from this file. The standard buy path is an account **API key + deposited GB** (`X-API-Key`, REST/MCP - covers both Pool Gateway and Dedicated Ports); x402 USDC (Dedicated Port via `/v1/x402/proxy`, Pool Gateway Access via `/v1/x402/pool`, session management via `X-Session-Token` endpoints under `/v1/x402/manage/*`) is the optional wallet-only alternative: - Master skill file: https://agents.proxies.sx/skill.md - Dedicated Port: https://agents.proxies.sx/marketplace/skill.md - Pool Gateway Access: https://agents.proxies.sx/pool/skill.md ### Social | Platform | URL | |----------|-----| | Twitter/X | https://x.com/sxproxies | | Telegram | https://t.me/proxies_sx | ### Framework Integrations | Framework | Usage | Description | |-----------|-------|-------------| | MCP | `npx @proxies-sx/mcp-server` | 70 proxy tools for Claude/Cursor (API-key mode; wallet-only x402 mode exposes 19) | ### GitHub | Repository | Description | |------------|-------------| | https://github.com/bolivian-peru/proxies-sx-mcp-server | MCP server for Claude | | https://github.com/bolivian-peru/x402-sdk | x402 payment protocol SDK | | https://github.com/bolivian-peru/android-peer-sdk | Android SDK for bandwidth sharing | ### Peer SDK downloads (drop-in, same protocol) | File | Language | Run | |------|----------|-----| | https://agents.proxies.sx/peer/reference-sdk.js?v=1.6.6 | Node.js (reference) | `API_KEY=psx_... node reference-sdk.js` | | https://agents.proxies.sx/peer/go/ | Go 1.6.8 headless Linux x86-64 | Standalone package + checksum + service installer; source build optional | | https://agents.proxies.sx/peer/windows/releases/1.6.6/proxies-peer-windows-node-1.6.6.zip | Windows (Node app) | unzip -> setup.bat -> start.bat | ### NPM Packages | Package | URL | |---------|-----| | @proxies-sx/mcp-server | https://www.npmjs.com/package/@proxies-sx/mcp-server | --- ## Appendix: Legacy Buffered HTTP Flow (DEPRECATED) The original protocol buffered whole HTTP requests as JSON: the relay sent `proxy_request` (`{requestId, method, url, headers, body}` with base64 body), and the peer replied `proxy_response` (`{requestId, statusCode, headers, body}` base64) or `proxy_error` (`{requestId, error}`). This flow is replaced by the streaming tunnel protocol in Step 5 for both HTTP and HTTPS. Do NOT build a new client on `proxy_request` - it is slower (buffered, base64), and current customer traffic flows over `tunnel_connect` + tunnel data. Existing legacy peers still work, but new implementations must speak the tunnel protocol (binary-v1 preferred) to earn normally. --- ## Changelog - **2026-07-02**: **PROTOCOL DOC CORRECTIONS (code-verified)** - Heartbeat direction fixed: the PEER sends `heartbeat` every 30s and the relay replies `heartbeat_ack` (the relay never pings; 120s of silence = zombie-sweep force-close). Connection pool documented correctly: up to 6 WebSockets per device, over-cap evicts the oldest with close 4008 (4003 is no longer emitted). Backpressure cap is 16MB sustained 10s. Probe cron is every 5 min (separate throughput sweep every 30 min); added the `throughputContract` customer-routable floor (default 500 KB/s) to the register response. `my-devices`/`my-preferences` routes clarified as ACCOUNT auth (X-API-Key or account JWT - not the registration JWT). - **2026-05-21**: **MULTI-REGION RELAY REDIRECT** - The platform runs regional relays (EU + US, more coming) and routes each peer to the nearest. Advertise `supportsRelayRedirect: true` in `device_info` and handle the `relay_redirect` message to let the server move you to the closest relay at runtime (zero re-install on new regions). Connect to `register.relay`, not a hardcoded host. Reference SDK v1.3.0 implements this. Old peers are never redirected (stay on their first relay). - **2026-04-23**: **BINARY TUNNEL PROTOCOL (v1)** - Peers can now advertise `protocol: "binary-v1"` in `device_info` to opt into binary WebSocket frames for tunnel_data. Eliminates base64 + JSON envelope overhead on hot path. Legacy JSON+base64 still supported unchanged. Expect ~30% throughput improvement on top of streaming. - **2026-04-23**: **STREAMING HTTP** - Replaced the buffered `proxy_request`/`proxy_response` path with streaming TCP tunnels for HTTP (same as HTTPS). Throughput up from ~0.3 Mbps to 2-11 Mbps per peer; TTFB down from 1.8s to 0.6s p50; 1+ MB downloads now reliable. - **2026-04-16**: **MARKETPLACE LISTING** - Devices can now be listed for sale in pool gateway. Automated verification system checks IP quality, ISP, VPN/proxy detection. Quality score 0-100. Admin or auto-approval workflow. Anti-fraud protections. - **2026-04-16**: **API KEY AUTO-LINK** - Registration now accepts optional `apiKey` field to auto-link devices to farmer accounts. Also accepts `X-API-Key` header. - **2026-04-16**: **HTTPS TUNNEL SUPPORT** - Documented tunnel_connect/tunnel_data/tunnel_closed message types for HTTPS proxying via TCP tunnels. - **2026-04-16**: **RELAY FIX** - Fixed dotenv load order bug that prevented internal gateway auth. Fixed `http_response` -> `proxy_response` message type (legacy `http_response` still accepted). - **2026-02-17**: **SECURITY HARDENING** - All SDK device endpoints now require JWT authentication (wallet, payout, earnings, device details). Free JWT endpoint removed (replaced with refresh token flow). Rate limiting added to device registration. Auth guards now check token revocation against DB in real-time. - **2026-02-05**: Added freestyle philosophy - agents encouraged to invent their own methods - **2026-02-05**: Peer Account Portal (peer-auth/peer-account endpoints) marked as coming soon - not yet deployed - **2026-02-04**: Browser API now includes auto-allocated mobile proxy (DE/GB/FR/ES/PL/US) - no need to provide your own - **2026-02-04**: Added x402 Mobile Proxy management endpoints (replace, topup, calculate) - **2026-02-02**: Security update - JWT reduced to 1 hour, refresh tokens added - **2026-02-02**: WebSocket auth moved from URL to headers (Sec-WebSocket-Protocol) - **2026-02-02**: Connection limits added (max 2 per device) - **2026-02-02**: Message rate limiting added (100/min) - **2026-02-02**: Wallet security - 7-day cooling period after changes - **2026-02-02**: IP classification now fully server-side (device-reported ignored) - **2026-02-02**: All IP types now earn - Datacenter IPs earn the base-tier rate - **2026-02-02**: Initial release with IP classification and fraud prevention ## Peer release status — October 6, 2026 Go/Linux 1.6.8 and Node reference/Windows Node 1.6.6 retain the latest authenticated server-assigned relay in the existing fallback cycle and save accepted redirects atomically in the existing identity file. After a temporary relay outage, retries can return to that assigned relay; restarting the SDK retains the saved assignment. Manual relay pins, healthy sibling protection, target safety, Retry-After and exponential backoff remain. Node reference/Windows Node 1.6.6 also waits at least as long as a registration 429 asks (Retry-After header, or retryAfterSeconds/retryAfter in the JSON body) and stops retrying a registration the server refuses (HTTP 400/401/403: wrong or inactive key, unsigned agreement, deactivated key): it prints one [ACTION NEEDED] line saying what to fix and checks again about every 30 minutes. New configurations default to one socket; explicit connection settings are preserved. Update one stopped device first, keeping its original key, name, configuration and state. Installed devices are not updated automatically. The separate Windows native executable is unchanged; Node tests do not establish Windows-specific socket or service behavior. CONNECTED/ACK confirms admission, not listing, customer traffic or earnings. This SDK release does not establish that server recovery is complete.