diff --git a/deploy/gen-resell-operator.sh b/deploy/gen-resell-operator.sh new file mode 100755 index 0000000..47939fe --- /dev/null +++ b/deploy/gen-resell-operator.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# Generate a full-blueprint "resell" operator: a systemd unit + an install/register +# runbook for one operator whose inference backend IS the Tangle Router (it resells +# Tangle's centralized models at a discount). Run it once per operator (the live +# fleet already uses indices/ports 3=9500 and 4=9400, so start fresh ones at 5+). +# +# WHY this is a valid resell (not the forbidden mode): the operator points +# INFERENCE_BAZAAR_INFERENCE_URL at the router with its own key → backend +# mode="external" (operator/src/inference.rs), which PASSES the bonded-issuer +# fail-closed check (venue.rs:151). The naked ROUTER_URL fallback would be +# mode="router" and is refused for an issuer. +# +# What you must provide (NOT in the repo — human-gated): +# OP_N unique index for paths/unit name (e.g. 5) +# PORT unique local HTTP port (e.g. 9700) +# SERVICE_ID the on-chain service id (assigned at request-service/approve) +# OPERATOR_KEY attester/signing key, 0x-hex (its address must be a book attester) +# SUBMITTER_KEY gas/submitter key, 0x-hex (keep distinct from OPERATOR_KEY) +# ROUTER_API_KEY this operator's OWN Tangle Router API key (the resell credential) +# INSTRUMENT market to make, e.g. anthropic/claude-opus-4-8:output +# +# Usage: +# OP_N=5 PORT=9700 SERVICE_ID=5 OPERATOR_KEY=0x.. SUBMITTER_KEY=0x.. \ +# ROUTER_API_KEY=tngl-.. INSTRUMENT=anthropic/claude-opus-4-8:output \ +# deploy/gen-resell-operator.sh +set -euo pipefail +cd "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +: "${OP_N:?set OP_N (unique operator index, e.g. 5)}" +: "${PORT:?set PORT (unique local HTTP port, e.g. 9700)}" +: "${OPERATOR_KEY:?set OPERATOR_KEY (0x-hex attester key)}" +: "${ROUTER_API_KEY:?set ROUTER_API_KEY -- this operator Tangle Router API key}" +SUBMITTER_KEY="${SUBMITTER_KEY:-$OPERATOR_KEY}" +INSTRUMENT="${INSTRUMENT:-anthropic/claude-opus-4-8:output}" +SERVICE_ID="${SERVICE_ID:-}" +ROUTER_URL="${ROUTER_URL:-https://router.tangle.tools}" +# Default to the tsUSD rail the live fleet uses; override for the USDC rail. +SETTLEMENT_ADDR="${INFERENCE_BAZAAR_SETTLEMENT_ADDR:-0x64867eacf2e4581d182c2Be634cfD7fF3D3d9f83}" +BLUEPRINT_ID="${BLUEPRINT_ID:-17}" +ROOT="/opt/inference-bazaar${OP_N}" +UNIT="deploy/hetzner/inference-bazaar${OP_N}-blueprint-runtime.service" + +cat > "$UNIT" </dev/null && cast wallet address --private-key "$OPERATOR_KEY" 2>/dev/null || echo "") + +cat < wrote ${UNIT} (operator ${OP_N}, port ${PORT}, reselling ${ROUTER_URL} as ${INSTRUMENT}) + operator address: ${OP_ADDR} + +Runbook to bring it live (human-gated steps marked ⚠): + +1. ⚠ Fund the operator: send gas + the collateral token to ${OP_ADDR}, then post + collateral: COLLATERAL_AMOUNT=20000000 OPERATOR_KEY=${OPERATOR_KEY} \\ + deploy/onboard-operator.sh +2. ⚠ Register + request the service on-chain (needs the funded key + cargo-tangle): + cargo tangle blueprint register --blueprint-id ${BLUEPRINT_ID} + cargo tangle blueprint request-service --blueprint-id ${BLUEPRINT_ID} + then a governance approver (Safe) approves the request → note the SERVICE_ID + and re-run this generator with SERVICE_ID= to stamp the unit. +3. Install on the Hetzner box (178.104.232.124): + ssh root@178.104.232.124 'mkdir -p ${ROOT}/keystore ${ROOT}/data' + scp ${UNIT} root@178.104.232.124:/etc/systemd/system/ + ssh root@178.104.232.124 'systemctl daemon-reload && systemctl enable --now inference-bazaar${OP_N}-blueprint-runtime' +4. Add port ${PORT} to the quoter loop (deploy/hetzner/seed.sh PORTS) so it ticks. + +RUNBOOK