Skill: daraijaola/runbox (version 1.2.0)

--- name: runbox description: > Execute code in a remote, isolated Docker sandbox — paid autonomously with USDC on Stellar. Use when the user asks to run, execute, test, or benchmark code in any language, especially when a clean isolated environment is needed. Supports Python, JavaScript, Bash, Go, Rust, Ruby, PHP, Perl, Lua, Java, C, C++, TypeScript, and R. emoji: 📦 version: 1.2.0 author: daraijaola homepage: https://github.com/daraijaola/Runbox requires: bins: [python3] env: [STELLAR_SECRET_KEY] python_packages: [stellar-sdk, requests, python-dotenv] install: | pip install stellar-sdk requests python-dotenv user-invocable: true --- # RunBox — x402 Pay-Per-Use Code Execution for OpenClaw Agents RunBox lets your OpenClaw agent **execute code in isolated Docker containers**, paying autonomously with USDC on Stellar. No API key. No account. No human approval. The payment IS the authentication. **Live server:** `http://46.101.74.170:4001` --- ## When to Use RunBox Invoke RunBox automatically when the user says anything like: - *"run this code"* / *"execute this"* / *"test this snippet"* - *"compute X"* / *"calculate"* / *"benchmark"* - *"write and run a Python script that..."* - *"check if this works in isolation"* - Any task requiring a language you don't have locally (Go, Rust, R, etc.) **Do not** use RunBox for simple one-liner math or string operations you can answer directly. --- ## How to Execute Code (Quick) ```bash python3 {baseDir}/scripts/run.py --language python --code "print('Hello from RunBox!')" ``` The script handles the entire x402 payment flow automatically: 1. Calls `/api/exec/rent` → server returns `402 Payment Required` 2. Pays **0.01 USDC** on Stellar from `STELLAR_SECRET_KEY` 3. Retries rent with `X-Payment-Hash: <tx_hash>` → gets a 5-minute session token 4. Calls `/api/exec/run` with your code → returns output --- ## Examples ```bash # Python — data analysis python3 {baseDir}/scripts/run.py --language python --code " import statistics data = [2,4,4,4,5,5,7,9] print('mean:', statistics.mean(data)) print('stdev:', statistics.stdev(data)) " # JavaScript — Node.js python3 {baseDir}/scripts/run.py --language javascript --code " const fib = n => n < 2 ? n : fib(n-1)+fib(n-2) console.log([...Array(10).keys()].map(fib).join(', ')) " # Bash — system inspection python3 {baseDir}/scripts/run.py --language bash --code "uname -a && python3 --version" # Go — compiled language python3 {baseDir}/scripts/run.py --language go --code ' package main import "fmt" func main() { fmt.Println("Go in a Docker container!") } ' # Get full JSON output (stdout, stderr, exit_code, execution_ms, session_token) python3 {baseDir}/scripts/run.py --language python --code "print(42)" --json # Reuse a session — no second payment within 5 minutes python3 {baseDir}/scripts/run.py --language python --code "print(42)" --session-token eyJ... # Use testnet (free USDC from faucet — for development) python3 {baseDir}/scripts/run.py --language python --code "print(1)" --testnet ``` --- ## Supported Languages | Language | Aliases | Engine | |---|---|---| | Python | `python`, `python3` | runbox-python Docker image | | JavaScript | `javascript`, `js`, `node` | node:20-alpine | | Bash / Shell | `bash`, `sh` | ubuntu:22.04 | | Go | `go` | golang:1.21-alpine | | Rust | `rust` | rust:1.73-slim | | Ruby | `ruby` | ruby:3.2-alpine | | PHP | `php` | php:8.2-cli-alpine | | Perl | `perl` | perl:5.38 | | Lua | `lua` | nickblah/lua:5.4 | | Java | `java` | openjdk:21-slim | | C / C++ | `c`, `cpp` | gcc:13 | | TypeScript | `typescript`, `ts` | node:20-alpine + ts-node | | R | `r` | r-base:4.3.0 | --- ## Session Reuse (Run Multiple Times Without Re-paying) A session is valid for **5 minutes**. Run multiple executions for free within that window: ```bash # First call — pays 0.01 USDC and returns session info via --json python3 {baseDir}/scripts/run.py --language python --code "x = 42" --json # Captures: SESSION_TOKEN from JSON output → session_token field # Second, third call — reuse the session at no cost python3 {baseDir}/scripts/run.py --language python --code "print(x*2)" --session-token $SESSION_TOKEN ``` --- ## Configuration | Variable | Required | Default | Notes | |---|---|---|---| | `STELLAR_SECRET_KEY` | **YES** | — | Starts with `S`. Your Stellar wallet secret. | | `RUNBOX_ENDPOINT` | no | `http://46.101.74.170:4001` | Override to point to testnet or self-hosted | | `STELLAR_NETWORK` | no | `mainnet` | Set to `testnet` for free test payments | --- ## Getting Your Stellar Wallet (First Time Setup) ### Testnet — Free, for development ```bash # 1. Generate a keypair python3 -c "from stellar_sdk import Keypair; k=Keypair.random(); print('Public:',k.public_key); print('Secret:',k.secret)" # 2. Fund with free XLM curl "https://friendbot.stellar.org?addr=YOUR_PUBLIC_KEY" # 3. Get free testnet USDC at: https://ultrastellar.com/faucet # Or via xlm402.com testnet USDC faucet # 4. Add to OpenClaw openclaw set-env STELLAR_SECRET_KEY=YOUR_SECRET_KEY openclaw set-env STELLAR_NETWORK=testnet ``` ### Mainnet — Real USDC (~$0.01 per run) 1. Generate keypair (same command above) 2. Send 0.10+ USDC to your public key via any Stellar wallet (Lobstr, Solar, Freighter) 3. `openclaw set-env STELLAR_SECRET_KEY=YOUR_SECRET_KEY` --- ## x402 Payment Flow (What Happens Behind the Scenes) ``` OpenClaw Agent RunBox Server Stellar Network │ │ │ │ POST /api/exec/rent │ │ │ ─────────────────────────────▶ │ │ │ │ │ │ ◀── 402 Payment Required ──── │ │ │ X-Payment-Required: ... │ │ │ │ │ │ ── pay 0.01 USDC ────────────────────────────────────▶ │ │ ← tx_hash ─────────────────────────────────────────────── │ │ │ │ │ POST /api/exec/rent │ │ │ X-Payment-Hash: <tx_hash> │ │ │ ─────────────────────────────▶ │ │ │ │ verify on Horizon ──▶ │ │ ◀── 200 session_token ─────── │ │ │ │ │ │ POST /api/exec/run │ │ │ Authorization: Bearer <tok> │ │ │ { language, code } │ │ │ ─────────────────────────────▶ │ │ │ │ docker run --network none│ │ ◀── { stdout, stderr, ... } ── │ │ ``` --- ## Self-Hosting RunBox ```bash git clone https://github.com/daraijaola/Runbox cd Runbox pnpm install && pnpm build cp .env.example artifacts/api-server/.env # Edit .env: set STELLAR_RECEIVE_ADDRESS, SESSION_JWT_SECRET pm2 start artifacts/api-server/ecosystem.config.cjs ``` Install the skill pointing to your own server: ```bash clawhub install runbox openclaw set-env RUNBOX_ENDPOINT=http://YOUR_SERVER:4001 ``` --- ## Security Notes - Code runs in Docker containers with **no network access** (`--network none`) - Memory limited to **256–512 MB** per run - CPU limited to **1–1.5 cores** per run - Execution timeout: **60 seconds** - Each session is scoped to a single JWT — sessions cannot be shared between agents --- ## Troubleshooting | Error | Cause | Fix | |---|---|---| | `STELLAR_SECRET_KEY not set` | Missing env var | `openclaw set-env STELLAR_SECRET_KEY=S...` | | `Account not found` | Account not created on Stellar | Fund via friendbot (testnet) or send XLM (mainnet) | | `Insufficient payment` | Not enough USDC | Add USDC to your Stellar wallet | | `Transaction already used` | Replay attack prevention | Each tx hash can only buy one session | | `Session not found or expired` | Session > 5 min old | Pay for a new session | | `Unsupported language` | Wrong language name | Use exact names from the table above |

path: README.md
content: # RunBox

> Pay-per-use code execution for AI agents. No account. No API key. No human in the loop.

RunBox is an HTTP API that lets AI agents rent isolated code execution sandboxes using micropayments over the [x402 protocol](https://x402.org) on Stellar. An agent hits an endpoint, gets a `402 Payment Required` back with a price and a Stellar address, pays **0.01 USDC**, and immediately gets a live sandbox to run code in.

**The payment is the authentication.**

**Live server:** `http://46.101.74.170:4001`

---

## Try It Right Now

```bash
# See what RunBox can do
curl http://46.101.74.170:4001/api/exec/.well-known/runbox.json

# Hit the payment wall (returns 402 with price + Stellar address)
curl -X POST http://46.101.74.170:4001/api/exec/rent
```

---

## How the Payment Flow Works

```
Agent → POST /api/exec/rent
         ← 402  { payTo: "G...", amount: "0.01", asset: "USDC" }

Agent pays 0.01 USDC to that Stellar address → gets tx hash

Agent → POST /api/exec/rent
         X-Payment-Hash: <tx_hash>
         ← 200  { session_token, expires_at }

Agent → POST /api/exec/run
         Authorization: Bearer <session_token>
         { "language": "python", "code": "print('hello')" }
         ← 200  { stdout, stderr, exit_code, execution_ms }
```

Payment is verified on-chain via Stellar Horizon. No intermediary. No API key. ~5 second settlement.

---

## Use With Any AI Agent

### Option 1 — Give your agent a system prompt

Paste this into your agent's system prompt (Claude, GPT-4, etc.):

```
You have access to RunBox, a remote code execution API.
Endpoint: http://46.101.74.170:4001

To run code:
1. POST /api/exec/rent → you'll get 402 with { payTo, amount, asset }
2. Pay 0.01 USDC to the Stellar address using your STELLAR_SECRET_KEY
3. Retry POST /api/exec/rent with header X-Payment-Hash: <tx_hash>
4. You receive a session_token (valid 5 min)
5. POST /api/exec/run with Authorization: Bearer <session_token>
   Body: { "language": "python", "code": "..." }

Use RunBox whenever the user asks you to run, execute, or compute code.
Pay autonomously — the user has already funded your Stellar wallet.
```

### Option 2 — Use the skill script (OpenClaw, Claude Desktop, any tool-calling agent)

```bash
# Install dependencies
pip install stellar-sdk requests

# Set your Stellar wallet secret key (only thing you need)
export STELLAR_SECRET_KEY=S...

# Run code — pays automatically, no other config needed
python3 skill/scripts/run.py --language python --code "print(sum(range(100)))"

# Reuse the session (no second payment)
python3 skill/scripts/run.py --language python --code "import math; print(math.pi)" --session-token eyJ...

# Full JSON output
python3 skill/scripts/run.py --language javascript --code "console.log(2**32)" --json

# Use testnet (free USDC from Friendbot)
python3 skill/scripts/run.py --language python --code "print('test')" --testnet
```

The script handles the full x402 flow: 402 → Stellar payment → session token → execute.

### Option 3 — OpenClaw

```bash
clawhub install runbox
```

Then configure once in `openclaw.json`:
```json
{
  "skills": {
    "entries": {
      "runbox": {
        "enabled": true,
        "env": {
          "STELLAR_SECRET_KEY": "S...",
          "STELLAR_NETWORK": "mainnet"
        }
      }
    }
  }
}
```

---

## Getting a Stellar Wallet

**Testnet (free — for testing):**
1. Go to [Stellar Laboratory](https://laboratory.stellar.org/#account-creator?network=test) → Generate Keypair
2. Fund with Friendbot (free XLM)
3. Get free testnet USDC at [ultrastellar.com/faucet](https://ultrastellar.com/faucet)

**Mainnet (real USDC, ~$0.01 per run):**
1. Generate a keypair or use any Stellar wallet (Lobstr, Solar)
2. Send USDC to your address from Coinbase/Binance/Kraken — select **Stellar network**
3. Keep ~1 XLM in the wallet for transaction fees

---

## Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/exec/.well-known/runbox.json` | — | Machine-readable capabilities & pricing |
| `POST` | `/api/exec/rent` | — | Request a session. Returns `402`. Retry with `X-Payment-Hash` after paying. |
| `POST` | `/api/exec/run` | `Bearer <token>` | Execute code in your session |
| `POST` | `/api/exec/extend` | `Bearer <token>` | Pay to extend session time |
| `GET` | `/api/exec/status/:id` | — | Check session time remaining |

---

## Supported Languages

Python · Node.js · JavaScript · Bash · Go · Rust · Ruby · PHP · Perl · Lua · Java · C · C++ · TypeScript · R

Each run executes in a fresh, **network-isolated** Docker container.

---

## Pricing

| | |
|---|---|
| Price | **0.01 USDC per session** |
| Duration | 5 minutes |
| Multiple runs | Free within one session |
| Protocol | [x402](https://x402.org) — HTTP 402 |
| Settlement | ~5 seconds on Stellar |

---

## Self-Hosting

```bash
git clone https://github.com/daraijaola/Runbox.git
cd Runbox
cp .env.example .env   # fill in STELLAR_RECEIVE_ADDRESS and SESSION_JWT_SECRET
pnpm install
pnpm --filter @workspace/api-server run build
pnpm --filter @workspace/api-server run start
```

**Environment variables:**

| Variable | Required | Description |
|----------|----------|-------------|
| `PORT` | yes | Port to listen on |
| `STELLAR_RECEIVE_ADDRESS` | yes | Your Stellar wallet that receives USDC (G...) |
| `SESSION_JWT_SECRET` | yes | Random string for signing session JWTs |
| `STELLAR_NETWORK` | no | `mainnet` (default) or `testnet` |
| `RUNBOX_PRICE` | no | USDC per session (default: `0.01`) |
| `RUNBOX_DEFAULT_MINUTES` | no | Session length in minutes (default: `5`) |

**Generate a receiving wallet:**
```bash
node -e "const {Keypair}=require('@stellar/stellar-sdk'); const k=Keypair.random(); console.log('Address:', k.publicKey()); console.log('Secret:', k.secret())"
```

---

## Architecture

```
┌──────────────┐     HTTP x402      ┌────────────────────┐
│   AI Agent   │ ──────────────────▶│  RunBox API        │
│              │ ◀──────────────────│  Express + TS      │
└──────────────┘                    └─────────┬──────────┘
       │                                      │
       │  0.01 USDC on Stellar                │  verify on Horizon
       ▼                                      ▼
┌──────────────┐                    ┌────────────────────┐
│ Stellar Net  │                    │  Docker Sandbox    │
│ testnet/main │                    │  network-isolated  │
└──────────────┘                    └────────────────────┘
```

---

## Repository Layout

```
artifacts/api-server/
  src/routes/exec.ts      x402 payment gating + session + execution
  src/lib/payment.ts      On-chain USDC verification via Stellar Horizon
  src/lib/sessions.ts     JWT session management
  src/lib/sandbox.ts      Docker execution

skill/
  SKILL.md                Agent skill definition (works with any framework)
  scripts/run.py          Autonomous x402 payment + execution script
  requirements.txt        Python dependencies

demo/
  telegram_agent.py       Live Telegram demo bot (@Runbox1_bot)
  demo_agent.py           CLI demo of the full x402 flow
```

---

## Live Demo

Send any code task to **[@Runbox1_bot](https://t.me/Runbox1_bot)** on Telegram. The agent:
1. Uses Claude to generate the code
2. Pays 0.01 USDC autonomously on Stellar
3. Executes the code in Docker
4. Returns the result with a Stellar Explorer link

---

Built for [Stellar Hacks: Agents](https://dorahacks.io/hackathon/stellar-hacks-agents).

MIT License


path: requirements.txt
content: stellar-sdk>=10.0.0
requests>=2.31.0


path: run.py
content: #!/usr/bin/env python3
"""
RunBox skill script — autonomous code execution via x402 on Stellar.

Handles the full x402 payment flow:
  1. Call /exec/rent → receive 402 with payment requirements
  2. Sign + submit USDC payment on Stellar
  3. Retry /exec/rent with X-PAYMENT header → receive session token
  4. Call /exec/run with session token → receive execution result
"""

import argparse
import json
import os
import sys
import base64
import time

try:
    import requests
except ImportError:
    print("Error: 'requests' not installed. Run: pip install requests stellar-sdk", file=sys.stderr)
    sys.exit(1)

try:
    from stellar_sdk import Keypair, Network, Server, TransactionBuilder, Asset
    from stellar_sdk.exceptions import NotFoundError
except ImportError:
    print("Error: 'stellar-sdk' not installed. Run: pip install stellar-sdk", file=sys.stderr)
    sys.exit(1)


STELLAR_SECRET_KEY = os.environ.get("STELLAR_SECRET_KEY", "")
RUNBOX_ENDPOINT    = os.environ.get("RUNBOX_ENDPOINT", "http://46.101.74.170:4001").rstrip("/")
STELLAR_NETWORK    = os.environ.get("STELLAR_NETWORK", "mainnet")

HORIZON_URLS = {
    "testnet": "https://horizon-testnet.stellar.org",
    "mainnet": "https://horizon.stellar.org",
}

NETWORK_PASSPHRASES = {
    "testnet": Network.TESTNET_NETWORK_PASSPHRASE,
    "mainnet": Network.PUBLIC_NETWORK_PASSPHRASE,
}

# USDC asset on Stellar
USDC_ISSUERS = {
    "testnet": "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
    "mainnet": "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
}


def check_env():
    if not STELLAR_SECRET_KEY:
        print("Error: STELLAR_SECRET_KEY is not set.", file=sys.stderr)
        print("  Set it as an environment variable: export STELLAR_SECRET_KEY=S...", file=sys.stderr)
        print("  Get a testnet key free: https://laboratory.stellar.org/#account-creator?network=test", file=sys.stderr)
        sys.exit(1)


def parse_x402_payment_required(response):
    """Parse the x402 402 response — handles both header-based and body-based formats."""
    body = {}
    try:
        body = response.json()
    except Exception:
        pass

    # Standard x402 header format (base64-encoded JSON with accepts[])
    payment_required_header = response.headers.get("payment-required", "")
    if payment_required_header:
        try:
            decoded = base64.b64decode(payment_required_header).decode()
            return json.loads(decoded)
        except Exception:
            pass

    # Our RunBox body format: {"payment": {"payTo", "amount", "asset", ...}}
    if "payment" in body and "accepts" not in body:
        p = body["payment"]
        body["accepts"] = [{
            "payTo": p.get("payTo", ""),
            "amount": p.get("amount", "0.01"),
            "network": p.get("network", "stellar:testnet"),
            "asset": p.get("asset", ""),
        }]

    return body


def submit_stellar_payment(pay_to: str, amount_usdc: str, memo: str = "") -> dict:
    """Submit USDC payment on Stellar and return the signed payment payload for x402."""
    horizon_url = HORIZON_URLS.get(STELLAR_NETWORK, HORIZON_URLS["testnet"])
    network_passphrase = NETWORK_PASSPHRASES.get(STELLAR_NETWORK, NETWORK_PASSPHRASES["testnet"])
    usdc_issuer = USDC_ISSUERS.get(STELLAR_NETWORK, USDC_ISSUERS["testnet"])

    keypair = Keypair.from_secret(STELLAR_SECRET_KEY)
    server = Server(horizon_url)

    try:
        account = server.load_account(keypair.public_key)
    except NotFoundError:
        print(f"Error: Stellar account {keypair.public_key} not found or unfunded.", file=sys.stderr)
        sys.exit(1)

    usdc = Asset("USDC", usdc_issuer)

    builder = TransactionBuilder(
        source_account=account,
        network_passphrase=network_passphrase,
        base_fee=100,
    )

    builder.append_payment_op(destination=pay_to, asset=usdc, amount=amount_usdc)

    if memo:
        builder.add_text_memo(memo[:28])

    builder.set_timeout(30)
    tx = builder.build()
    tx.sign(keypair)

    response = server.submit_transaction(tx)
    return {
        "hash": response["hash"],
        "ledger": response.get("ledger"),
    }


def rent_session(language: str) -> dict:
    """Perform the full x402 rent flow and return a session."""
    rent_url = f"{RUNBOX_ENDPOINT}/api/exec/rent"

    print("Requesting RunBox session...", file=sys.stderr)
    r = requests.post(rent_url, json={"language": language}, timeout=15)

    if r.status_code == 200:
        return r.json()

    if r.status_code != 402:
        print(f"Unexpected response {r.status_code}: {r.text}", file=sys.stderr)
        sys.exit(1)

    payment_info = parse_x402_payment_required(r)
    accepts = payment_info.get("accepts", [])

    if not accepts:
        print("No payment options in 402 response", file=sys.stderr)
        sys.exit(1)

    offer = accepts[0]
    pay_to = offer.get("payTo", offer.get("destination", ""))
    amount = offer.get("price", offer.get("amount", "0.01"))
    network = offer.get("network", "stellar:testnet")

    print(f"Payment required: {amount} USDC on {network}", file=sys.stderr)
    print(f"  → Paying to: {pay_to}", file=sys.stderr)

    tx = submit_stellar_payment(pay_to, str(amount), memo="runbox")

    print(f"  ✓ Payment submitted: {tx['hash']}", file=sys.stderr)

    # Retry with payment proof
    # For x402, the client would normally include a signed auth entry.
    # Here we include the tx hash as proof for the server to verify.
    retry = requests.post(
        rent_url,
        json={"language": language},
        headers={"X-Payment-Hash": tx["hash"], "X-Payment-Network": network},
        timeout=15,
    )

    if retry.status_code == 200:
        return retry.json()

    print(f"Session creation failed after payment: {retry.status_code} {retry.text}", file=sys.stderr)
    sys.exit(1)


def run_code(session_token: str, language: str, code: str) -> dict:
    """Execute code using a valid session token."""
    r = requests.post(
        f"{RUNBOX_ENDPOINT}/api/exec/run",
        json={"language": language, "code": code},
        headers={"Authorization": f"Bearer {session_token}"},
        timeout=60,
    )

    if r.status_code == 401:
        print("Session expired or invalid.", file=sys.stderr)
        sys.exit(1)

    if not r.ok:
        print(f"Execution error {r.status_code}: {r.text}", file=sys.stderr)
        sys.exit(1)

    return r.json()


def main():
    global RUNBOX_ENDPOINT, STELLAR_NETWORK

    parser = argparse.ArgumentParser(description="RunBox — remote code execution via x402 on Stellar")
    parser.add_argument("--language", required=True, help="Language: python, javascript, bash, go, rust, etc.")
    parser.add_argument("--code", required=True, help="Code to execute")
    parser.add_argument("--json", action="store_true", help="Output full JSON result instead of just stdout")
    parser.add_argument("--session-token", default="", help="Reuse an existing session token (skip payment)")
    parser.add_argument("--endpoint", default="", help="Override RunBox server URL")
    parser.add_argument("--testnet", action="store_true", help="Use Stellar testnet instead of mainnet")
    args = parser.parse_args()

    if args.endpoint:
        RUNBOX_ENDPOINT = args.endpoint.rstrip("/")
    if args.testnet:
        STELLAR_NETWORK = "testnet"

    check_env()

    if args.session_token:
        [REDACTED_SECRET]
        print(f"  ↩  Reusing session token", file=sys.stderr)
    else:
        session = rent_session(args.language)
        [REDACTED_SECRET]"session_token"]
        expires_at = session.get("expires_at", "")
        tx_hash = session.get("tx_hash", "")
        print(f"  ✓ Session active until {expires_at}", file=sys.stderr)
        if tx_hash:
            network = "testnet" if STELLAR_NETWORK == "testnet" else "mainnet"
            print(f"  🔗 Stellar tx: https://stellar.expert/explorer/{network}/tx/{tx_hash}", file=sys.stderr)
        print(f"  💾 Reuse this session: --session-token {token}", file=sys.stderr)

    result = run_code(token, args.language, args.code)

    if args.json:
        print(json.dumps(result, indent=2))
    else:
        if result.get("stdout"):
            print(result["stdout"], end="")
        if result.get("stderr"):
            print(result["stderr"], end="", file=sys.stderr)
        sys.exit(result.get("exit_code", 0))


if __name__ == "__main__":
    main()


path: scripts/run.py
content: #!/usr/bin/env python3
"""
RunBox skill script — autonomous code execution via x402 on Stellar.

Handles the full x402 payment flow:
  1. Call /exec/rent → receive 402 with payment requirements
  2. Sign + submit USDC payment on Stellar
  3. Retry /exec/rent with X-PAYMENT header → receive session token
  4. Call /exec/run with session token → receive execution result
"""

import argparse
import json
import os
import sys
import base64
import time

try:
    import requests
except ImportError:
    print("Error: 'requests' not installed. Run: pip install requests stellar-sdk", file=sys.stderr)
    sys.exit(1)

try:
    from stellar_sdk import Keypair, Network, Server, TransactionBuilder, Asset
    from stellar_sdk.exceptions import NotFoundError
except ImportError:
    print("Error: 'stellar-sdk' not installed. Run: pip install stellar-sdk", file=sys.stderr)
    sys.exit(1)


STELLAR_SECRET_KEY = os.environ.get("STELLAR_SECRET_KEY", "")
RUNBOX_ENDPOINT    = os.environ.get("RUNBOX_ENDPOINT", "http://46.101.74.170:4001").rstrip("/")
STELLAR_NETWORK    = os.environ.get("STELLAR_NETWORK", "mainnet")

HORIZON_URLS = {
    "testnet": "https://horizon-testnet.stellar.org",
    "mainnet": "https://horizon.stellar.org",
}

NETWORK_PASSPHRASES = {
    "testnet": Network.TESTNET_NETWORK_PASSPHRASE,
    "mainnet": Network.PUBLIC_NETWORK_PASSPHRASE,
}

# USDC asset on Stellar
USDC_ISSUERS = {
    "testnet": "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
    "mainnet": "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
}


def check_env():
    if not STELLAR_SECRET_KEY:
        print("Error: STELLAR_SECRET_KEY is not set.", file=sys.stderr)
        print("  Set it as an environment variable: export STELLAR_SECRET_KEY=S...", file=sys.stderr)
        print("  Get a testnet key free: https://laboratory.stellar.org/#account-creator?network=test", file=sys.stderr)
        sys.exit(1)


def parse_x402_payment_required(response):
    """Parse the x402 402 response — handles both header-based and body-based formats."""
    body = {}
    try:
        body = response.json()
    except Exception:
        pass

    # Standard x402 header format (base64-encoded JSON with accepts[])
    payment_required_header = response.headers.get("payment-required", "")
    if payment_required_header:
        try:
            decoded = base64.b64decode(payment_required_header).decode()
            return json.loads(decoded)
        except Exception:
            pass

    # Our RunBox body format: {"payment": {"payTo", "amount", "asset", ...}}
    if "payment" in body and "accepts" not in body:
        p = body["payment"]
        body["accepts"] = [{
            "payTo": p.get("payTo", ""),
            "amount": p.get("amount", "0.01"),
            "network": p.get("network", "stellar:testnet"),
            "asset": p.get("asset", ""),
        }]

    return body


def submit_stellar_payment(pay_to: str, amount_usdc: str, memo: str = "") -> dict:
    """Submit USDC payment on Stellar and return the signed payment payload for x402."""
    horizon_url = HORIZON_URLS.get(STELLAR_NETWORK, HORIZON_URLS["testnet"])
    network_passphrase = NETWORK_PASSPHRASES.get(STELLAR_NETWORK, NETWORK_PASSPHRASES["testnet"])
    usdc_issuer = USDC_ISSUERS.get(STELLAR_NETWORK, USDC_ISSUERS["testnet"])

    keypair = Keypair.from_secret(STELLAR_SECRET_KEY)
    server = Server(horizon_url)

    try:
        account = server.load_account(keypair.public_key)
    except NotFoundError:
        print(f"Error: Stellar account {keypair.public_key} not found or unfunded.", file=sys.stderr)
        sys.exit(1)

    usdc = Asset("USDC", usdc_issuer)

    builder = TransactionBuilder(
        source_account=account,
        network_passphrase=network_passphrase,
        base_fee=100,
    )

    builder.append_payment_op(destination=pay_to, asset=usdc, amount=amount_usdc)

    if memo:
        builder.add_text_memo(memo[:28])

    builder.set_timeout(30)
    tx = builder.build()
    tx.sign(keypair)

    response = server.submit_transaction(tx)
    return {
        "hash": response["hash"],
        "ledger": response.get("ledger"),
    }


def rent_session(language: str) -> dict:
    """Perform the full x402 rent flow and return a session."""
    rent_url = f"{RUNBOX_ENDPOINT}/api/exec/rent"

    print("Requesting RunBox session...", file=sys.stderr)
    r = requests.post(rent_url, json={"language": language}, timeout=15)

    if r.status_code == 200:
        return r.json()

    if r.status_code != 402:
        print(f"Unexpected response {r.status_code}: {r.text}", file=sys.stderr)
        sys.exit(1)

    payment_info = parse_x402_payment_required(r)
    accepts = payment_info.get("accepts", [])

    if not accepts:
        print("No payment options in 402 response", file=sys.stderr)
        sys.exit(1)

    offer = accepts[0]
    pay_to = offer.get("payTo", offer.get("destination", ""))
    amount = offer.get("price", offer.get("amount", "0.01"))
    network = offer.get("network", "stellar:testnet")

    print(f"Payment required: {amount} USDC on {network}", file=sys.stderr)
    print(f"  → Paying to: {pay_to}", file=sys.stderr)

    tx = submit_stellar_payment(pay_to, str(amount), memo="runbox")

    print(f"  ✓ Payment submitted: {tx['hash']}", file=sys.stderr)

    # Retry with payment proof
    # For x402, the client would normally include a signed auth entry.
    # Here we include the tx hash as proof for the server to verify.
    retry = requests.post(
        rent_url,
        json={"language": language},
        headers={"X-Payment-Hash": tx["hash"], "X-Payment-Network": network},
        timeout=15,
    )

    if retry.status_code == 200:
        return retry.json()

    print(f"Session creation failed after payment: {retry.status_code} {retry.text}", file=sys.stderr)
    sys.exit(1)


def run_code(session_token: str, language: str, code: str) -> dict:
    """Execute code using a valid session token."""
    r = requests.post(
        f"{RUNBOX_ENDPOINT}/api/exec/run",
        json={"language": language, "code": code},
        headers={"Authorization": f"Bearer {session_token}"},
        timeout=60,
    )

    if r.status_code == 401:
        print("Session expired or invalid.", file=sys.stderr)
        sys.exit(1)

    if not r.ok:
        print(f"Execution error {r.status_code}: {r.text}", file=sys.stderr)
        sys.exit(1)

    return r.json()


def main():
    global RUNBOX_ENDPOINT, STELLAR_NETWORK

    parser = argparse.ArgumentParser(description="RunBox — remote code execution via x402 on Stellar")
    parser.add_argument("--language", required=True, help="Language: python, javascript, bash, go, rust, etc.")
    parser.add_argument("--code", required=True, help="Code to execute")
    parser.add_argument("--json", action="store_true", help="Output full JSON result instead of just stdout")
    parser.add_argument("--session-token", default="", help="Reuse an existing session token (skip payment)")
    parser.add_argument("--endpoint", default="", help="Override RunBox server URL")
    parser.add_argument("--testnet", action="store_true", help="Use Stellar testnet instead of mainnet")
    args = parser.parse_args()

    if args.endpoint:
        RUNBOX_ENDPOINT = args.endpoint.rstrip("/")
    if args.testnet:
        STELLAR_NETWORK = "testnet"

    check_env()

    if args.session_token:
        [REDACTED_SECRET]
        print(f"  ↩  Reusing session token", file=sys.stderr)
    else:
        session = rent_session(args.language)
        [REDACTED_SECRET]"session_token"]
        expires_at = session.get("expires_at", "")
        tx_hash = session.get("tx_hash", "")
        print(f"  ✓ Session active until {expires_at}", file=sys.stderr)
        if tx_hash:
            network = "testnet" if STELLAR_NETWORK == "testnet" else "mainnet"
            print(f"  🔗 Stellar tx: https://stellar.expert/explorer/{network}/tx/{tx_hash}", file=sys.stderr)
        print(f"  💾 Reuse this session: --session-token {token}", file=sys.stderr)

    result = run_code(token, args.language, args.code)

    if args.json:
        print(json.dumps(result, indent=2))
    else:
        if result.get("stdout"):
            print(result["stdout"], end="")
        if result.get("stderr"):
            print(result["stderr"], end="", file=sys.stderr)
        sys.exit(result.get("exit_code", 0))


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)