Hopwire is a Lightning-paid spot market for geographic network capacity. This repo starts with the control plane scaffold: a TanStack Start shell deployed on Vercel and a Convex backend for registry, demand, tunnel telemetry, provisioning jobs, reputation reports, and realtime state.
- TanStack Start for the app shell. The dashboard UI is intentionally deferred.
- Convex for the registry API, database, HTTP actions, and realtime subscriptions.
- Bun for package management and local commands.
- Vercel for the web deployment.
- Operator nodes, WireGuard, L402, and cloud provisioning workers run outside Vercel.
bun install
bun run devbun run dev starts Convex dev and the TanStack Start dev server. Convex will prompt for login and create the deployment variables on first run.
Seed demo backend state with:
bun run demo:seedReset demo backend state to a clean baseline with:
bun run demo:resetSmoke-test the public registry endpoints with:
bun run smoke:registrySimulate an operator registration and heartbeat with:
bun run operator:sim:once -- --region=icnSimulate an agent tunnel lifecycle with:
bun run tunnel:sim -- --region=nrt --category=standard-webUse the CLI wrapper for common registry operations:
bun run hopwire -- providers list --region=nrt
bun run hopwire -- demand record --region=bom --category=standard-web --max-price=260
bun run hopwire -- tunnel simulate --region=nrt --category=standard-webRun the local operator after configuring real Lexe Sidecar and WireGuard env vars:
bun run operator:preflight
bun run operator:devFor the payment demo, use one requester Lexe CLI wallet and one provider Lexe wallet per provider persona. Provider operators can use Sidecar credentials with HOPWIRE_LEXE_CLIENT_CREDENTIALS_PATH, or use HOPWIRE_LIGHTNING_BACKEND=lexe-cli for wallets created by lexe init. Requester commands always pay from the local Lexe CLI wallet.
Run a complete demo scenario with demand, simulated supply, tunnel start, meter ticks, and tunnel close:
bun run demo:scenario -- --region=icn --category=standard-webLeave the tunnel active for dashboard demos with:
bun run demo:scenario -- --region=gru --category=standard-web --leave-activeSee docs/user-workflows.md for the provider/requester UX, docs/api.md for the registry API contract, docs/demo-runbook.md for the backend demo sequence, docs/operator-architecture.md for the operator-side design, and docs/operator-deploy.md for a real VPS setup.
Agent workflow skills live in .agents/skills/hopwire-provider/SKILL.md and .agents/skills/hopwire-requester/SKILL.md.
Set CONVEX_DEPLOY_KEY in Vercel. The project includes vercel.json with:
{
"framework": "tanstack-start",
"installCommand": "bun install --frozen-lockfile",
"buildCommand": "bunx convex deploy --cmd-url-env-var-name VITE_CONVEX_URL --cmd 'bun run build'"
}Convex deploys backend functions first, injects the production Convex URL for the frontend build, then Vercel publishes the TanStack Start shell.
See docs/vercel-deploy.md for the import settings, environment variables, preview deployment setup, and CLI commands.
Convex HTTP Actions are exposed on the deployment's .convex.site domain.
GET /healthPOST /providers/registerPOST /providers/heartbeatGET /providers?region=nrt&category=standard-web&max_price_sats_per_minute=200GET /demand?region=gruGET /snapshotPOST /demandPOST /tunnels/startPOST /tunnels/meterPOST /tunnels/finishPOST /provision/jobsPOST /provision/jobs/updatePOST /reputation/report
Write endpoints are open by default for local prototype deployments. To require a shared write token, set REGISTRY_WRITE_TOKEN in the Convex deployment:
bunx convex env set REGISTRY_WRITE_TOKEN 'replace-with-a-random-token'Then set the same value in local .env.local for the simulator scripts. They send it as x-hopwire-registry-token.
Providers advertise explicit capabilities. egress means paid geographic network egress over WireGuard/L402. browser-cdp means the operator sells access to a fresh isolated Chromium instance controlled over a token-protected CDP proxy. Browser sessions use a new temporary profile per paid session; they do not expose or reuse the operator's personal browser.
Providers currently publish one broad access label, standard-web, in the existing categories field. Treat that field as a compatibility shim for now, not a detailed use-case taxonomy.
Provider pricing is capability-specific:
{
"capabilities": ["egress", "browser-cdp"],
"categories": ["standard-web"],
"pricing": {
"egressSatsPerMinute": 1,
"egressSatsPerMb": 1,
"browserSatsPerMinute": 1
}
}Providers may also publish policy metadata. If omitted, the registry normalizes reads to the mandatory prototype floor:
{
"policy": {
"blockedPorts": [25, 465, 587],
"blockedDestinationLists": ["known-abuse-destinations"],
"sniInspection": true,
"dnsInterception": true,
"dohDotBlocked": true,
"perAgentRateLimit": {
"requestsPerMinute": 60,
"mbPerMinute": 100
}
}
}This is registry metadata only. The real operator node still needs to enforce it at the tunnel host.
Start a paid remote browser session and export the values expected by browser-harness:
bun run hopwire -- browser start --region=blr --category=standard-web --sats=1 --envThen run browser-harness locally against the remote provider browser:
BU_NAME=hopwire-demo BU_CDP_WS='wss://operator.example.com/browser/...' browser-harness <<'PY'
new_tab("https://fast.com")
wait_for_load()
print(page_info())
PYWhen done, stop the paid provider session:
bun run hopwire -- browser stop --operator-url=http://operator.example.com:9443 --session-id=sess_...- Convex TanStack Start quickstart: https://docs.convex.dev/quickstart/tanstack-start
- Convex with TanStack Start: https://docs.convex.dev/client/tanstack/tanstack-start/
- Convex HTTP Actions: https://docs.convex.dev/functions/http-actions
- Convex with Vercel: https://docs.convex.dev/production/hosting/vercel
- Convex with Bun: https://docs.convex.dev/quickstart/bun
- TanStack Start on Vercel: https://vercel.com/docs/frameworks/full-stack/tanstack-start
- Bun install: https://bun.com/docs/pm/cli/install