Skip to content

OPoI Mining with cs-stratum-bridge

Regular pool mining (see Mining Pools) only earns you PoW block rewards. To also participate in OPoI — CS Coin's proof-of-inference protocol, where miners get paid for running AI inference requests — your cs-miner needs to connect through cs-stratum-bridge instead of connecting to a pool directly.

What the bridge does

cs-stratum-bridge sits between your cs-miner and everything else:

  • PoW mining.* traffic is relayed through, unchanged, to the real upstream pool — you still get normal pool payouts exactly as before.
  • OPoI traffic (opoi.assign / opoi.submit_result) is intercepted and owned entirely by the bridge, which talks directly to your csd node's JSON-RPC to drive the COMMIT → REVEAL → publish → payout lifecycle on-chain.

Your miner only ever talks to the bridge — never directly to csd or to the pool.

cs-miner  →  cs-stratum-bridge  →  upstream pool (PoW mining.* only)

                    └──  csd JSON-RPC (OPoI COMMIT/REVEAL, payouts)

Prerequisites

  1. A csd node you control, already running and reachable over RPC. The bridge needs server=1 and an rpcuser/rpcpassword set in that node's cs.conf:
    ini
    server=1
    rpcuser=stratumbridge
    rpcpassword=<a-strong-random-password>
    rpcallowip=127.0.0.1
    rpcport=26124
    Restart csd after editing cs.conf for it to take effect.
  2. An OPoI address with its private key already in that csd node's wallet. The bridge never holds or asks for a private key itself — it only ever calls the csd RPC to sign, so the key has to already live in the node's own wallet:
    bash
    # brand-new address — key is already in the wallet, nothing to import
    cs-cli getnewaddress
    
    # or, to reuse an address you already control elsewhere
    cs-cli importprivkey "<privkey>"
  3. Some CS balance in that wallet, to fund the OPoI stake collateral (see Funding the stake below). A brand-new node has zero balance until it mines or receives its first coins.

Download and run

Releases are published publicly, separately from the source (which stays closed). Grab the latest tarball for your platform from the cs-stratum-bridge-releases page, then:

bash
tar xzf cs-stratum-bridge-vX.Y.Z-linux-x86_64.tar.gz
cd cs-stratum-bridge-vX.Y.Z
./cs-stratum-bridge

There's no .env to hand-write. On first run, an interactive setup wizard detects what's missing and walks you through it:

cs-stratum-bridge: incomplete configuration — missing 6 variable(s):
  - UPSTREAM_POOL_ADDR
  - CSD_RPC_URL
  - CSD_RPC_USER
  - CSD_RPC_PASS
  - OPOI_ADDRESSES
  - OPOI_REQUESTER_API_KEY

How would you like to configure it?
  1) Manual — I'll exit here, you edit .env and run again
  2) Automatic — I'll ask for each value, test the connection to csd, and show the available UTXOs for the stake
> 2

Pick 2 (Automatic) unless you'd rather hand-edit .env yourself. It then prompts for each value:

VariableWhat to enter
UPSTREAM_POOL_ADDRhost:port of the real pool that handles PoW — for the official pool this is 127.0.0.1:3332 if it runs on the same machine, otherwise its public host:port
CSD_RPC_URLUsually http://127.0.0.1:26124 (the default it suggests)
CSD_RPC_USER / CSD_RPC_PASSThe rpcuser/rpcpassword from cs.conf above
OPOI_ADDRESSESThe address from Prerequisites step 2
OPOI_REQUESTER_API_KEYPress Enter to auto-generate one — only needed if you also plan to submit prompts through this bridge

Only ever run one instance per .env/data directory at a time. A second ./cs-stratum-bridge started while one is already running (e.g. in a different terminal) will collide on the embedded Postgres (bridge_pgdata) and the listening ports — this doesn't just fail to start, it can break the already-running instance's database connection too. Check first with ps aux | grep cs-stratum-bridge; if one is already up, just use it (tail -f its log, or hit its HTTP API) instead of starting another.

After that, it tests the RPC connection live:

Testing connection to csd at http://127.0.0.1:26124...
OK — csd responded, current height: 12345

Funding the stake

The OPoI protocol requires your address to lock a small amount of CS as stake collateral before it can start accepting work. Right after the RPC check, the wizard lists your wallet's spendable UTXOs and lets you pick one:

Fetching UTXOs from the csd wallet to use as stake collateral...

#    txid                                                               vout     amount (CS)   conf  address
1    5f3b2c...                                                          0             10.00000000    120  ta1q...

UTXO number to use as stake collateral (Enter to skip): 1
Using 5f3b2c...:0 as stake collateral.

If it prints "No UTXOs available yet", your wallet has zero balance — send it some CS, or mine a few blocks to it, then run ./cs-stratum-bridge again. Since the wizard is a no-op once .env is already complete, it re-appears only for this stake step until an active stake exists — you don't need to redo the earlier prompts.

Once a stake is picked (or already active), the bridge finishes booting and starts listening for miner connections:

INFO cs_stratum_bridge: cs-stratum-bridge starting listen_addr=0.0.0.0:3532 upstream_pool_addr=127.0.0.1:3332 ...
INFO cs_stratum_bridge: csd reachable height=12345

Connecting your miner

Point cs-miner at the bridge's LISTEN_ADDR (default 0.0.0.0:3532) instead of at the pool directly — everything else about the miner configuration (algorithm, wallet address, worker name) stays the same as in Getting Started with Mining.

Testing on testnet: submitting a real OPoI request

Requires cs-stratum-bridge v0.1.3+. Earlier versions never assign an OPEN-task request that uses a plain, non-manifested model name (e.g. gemma3:4b, as used below) — it silently sits at assigned_to: null forever. Grab the latest release if you're not sure which version you're running.

Once your miner is connected to the bridge and registered (its log shows miner registered 'expert' capability or similar), you can trigger a real end-to-end OPoI cycle yourself — useful to confirm everything before relying on it, and the same flow a requester's application would follow. This works on -testnet (or mainnet) — it does not work on a fresh node with zero balance, since the REQUEST transaction itself needs to be funded (see Prerequisites — you need the same kind of spendable balance the stake collateral needed).

You'll also need the bridge's OPOI_REQUESTER_API_KEY for step 2 — it was either auto-generated during the wizard (printed once, to the terminal, as Generated: <key>) or whatever you set by hand. If you didn't save it, read it back from the bridge's own .env:

bash
grep OPOI_REQUESTER_API_KEY .env

1. Broadcast the REQUEST on-chain. Only the SHA-256 hash of the prompt goes on-chain — never the prompt text itself:

bash
PROMPT="Explain blockchain in one sentence."
HASH=$(printf '%s' "$PROMPT" | sha256sum | cut -d' ' -f1)
REQID=$(cat /proc/sys/kernel/random/uuid)

cs-cli submitopoirequest "$REQID" "gemma3:4b" "$HASH" 128 1.0 <your-requester-address>

gemma3:4b here is just a model name (OPEN task type, the default) — it doesn't need to be a registered Model Manifest unless you want shard execution. 128 is max_tokens, 1.0 is the CS payment (testnet's floor is tiny — nOPoIFeeBase — so this is comfortably above the minimum).

2. Send the actual prompt to the bridge. This is the ONLY place the plaintext prompt ever travels — over HTTP, straight to the bridge, matched to the REQUEST by request_id:

bash
curl -X POST http://<bridge-host>:3533/cscoin/opoi/prompt \
  -H "x-opoi-api-key: <your OPOI_REQUESTER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d "{\"request_id\":\"$REQID\",\"prompt\":\"$PROMPT\"}"

A 404 request_id not found on-chain here means step 1's transaction hasn't confirmed yet — wait for a block, then re-run this exact curl command ($REQID/$PROMPT are still set in your shell from step 1, so the retry just works). Do not re-run the REQID=$(cat /proc/sys/kernel/random/uuid) line before retrying — that overwrites $REQID with a brand-new UUID that was never submitted on-chain at all, and every retry after that will 404 forever, no matter how many blocks pass. If you've lost track of which UUID you actually broadcast, it's right there in step 1's own JSON response ("request_id": "...") — copy it back into $REQID (or hardcode it directly in the curl below) instead of generating a new one.

3. Watch it resolve:

bash
cs-cli getopoirequest "$REQID"          # on-chain request state
curl http://<bridge-host>:3533/cscoin/opoi/pending   # bridge's in-flight queue

If your miner doesn't actually have the model available locally (no Ollama running, model never pulled), dispatch/commit still exercises the on-chain plumbing correctly, but generation itself will fail — check the miner's own log for the concrete error in that case.

Next steps

Released under the MIT License.