compute-resell

SkillCloud & infra

Lets your agent resell spare AI compute from Bankr, AWS Bedrock, and Google Vertex on a marketplace, auto-listing and pricing offers.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the compute-resell skill

About this capability

Autonomous compute-reselling agent on Surplus Intelligence - resells free or low-cost provider compute across Bankr, AWS Bedrock, and Google Vertex (GCP) as one skill. Reads the live market + your cost + your usage, auto-lists your models per enabled provider, and reactively prices each offer to win

What this skill tells your AI

The instructions your AI receives, as published by aeonfun/aeon in skills/compute-resell/SKILL.md and read by ahel’s review.

${var} - [<provider>:]<mode>. The optional <provider> prefix (bankr | aws | vertex) restricts the run to one provider; omit it to run every enabled provider (see Providers). <mode> selects the run mode - first match wins:

  • emptythe reactive run (the scheduled default): validate the wallet, read the live market + your cost + your usage, auto-list any missing models, reprice every offer against the current market, notify on signal. No separate setup step.
  • monitor → read-only market + offers + usage digest, no writes.
  • reprice → same as empty (kept as an explicit alias).
  • pause → soft-delete every offer (stop serving); state retained.
  • resume → re-create offers from state at the current reactive price.

Examples: empty = all enabled providers, reactive. aws = only AWS, reactive. vertex:pause = pause only Vertex. monitor = read-only digest across all enabled providers.

(There's no setup mode - the empty run bootstraps itself: if a provider has no offers yet and its credential is set, it lists that provider's models on the first run.) (You rarely need pause/resume by hand: the reactive run auto-reaps dead and orphaned offers and auto-relists healthy models - so removing a provider's credential secret already delists it cleanly, and adding a funded one back restores the lineup on the next run. Reach for pause/resume only for a deliberate manual hold.)

Providers

This skill is a single reselling engine run once per compute provider. All three providers sell on the same Surplus marketplace with the same create shape (POST /v1/seller/offers with {model, api_key, seller_base_url, ...}); they differ only in the compute source behind the offer - its credential, endpoint, cost basis, and per-account denylist. Everything downstream (market read, scoring, pricing, caps, adaptive discount, reaping, modes, notify) is provider-agnostic and identical for all three.

Provider adapter table - the only per-provider inputs:

dimbankrawsvertex
Surplus seller wallet (secret)SURPLUS_SELLER_KEYSURPLUS_SELLER_AWS_KEYSURPLUS_SELLER_VERTEX_KEY
provider credential → api_key (secret)BANKR_LLM_KEY (+ _2/_3)BEDROCK_API_KEYVERTEX_SERVICE_ACCOUNT_JSON (or VERTEX_API_KEY)
seller_base_urlhttps://llm.bankr.bot/v1BEDROCK_BASE_URL (repo var)built from VERTEX_PROJECT_ID + VERTEX_LOCATION (repo vars)
cost basis C (floor mode)/v1/prices bankr entryconfig bedrock_pricesconfig vertex_prices
config secretCOMPUTE_RESELL_CONFIGAWS_COMPUTE_RESELL_CONFIGVERTEX_COMPUTE_RESELL_CONFIG
state filememory/state/compute-resell.jsonmemory/state/aws-compute-resell.jsonmemory/state/vertex-compute-resell.json
preflight authX-API-Key (Bankr gateway)Authorization: Bearer (proxy)SA→access-token, or AIza key

A provider is "enabled" for a run iff its Surplus seller wallet secret and its provider credential secret are both set. Skip (don't error on) any provider missing either - a Bankr-only instance just sets SURPLUS_SELLER_KEY + BANKR_LLM_KEY and never touches AWS/Vertex.

The run loop. Resolve the provider set: [<provider>] from ${var} if it carries a prefix, else every enabled provider. Fetch the shared market reads once - GET /api/markets (roster) and GET /v1/prices (Surplus model catalog) are provider-agnostic, so read them a single time and reuse across providers. Then for each provider in the set, export RESELL_PROVIDER=<provider> (the helper scripts key their config secret + state file off it) and run the full engine below against that provider's adapter row - its own wallet, credential, seller_base_url, cost basis, config, and state file. Providers are independent: one provider failing (bad key, exhausted funds) never aborts the others; log its exit reason and continue. Aggregate one notify at the end (per-provider TLDR lines).

Never self-undercut across your own providers. Because all providers list on one order book, two of your own providers listing the same market id would undercut each other and drag your family's clearing price toward zero. The Cross-skill claim ledger (below) already prevents this: it globs memory/state/*compute-resell.json - which now includes all three provider state files - so each provider claims its models and a later provider in the loop skips any market id an earlier one already claimed this run. See Cross-skill claim ledger. The provider-specific credential, endpoint, cost basis, and denylist are detailed per adapter in Provider adapters at the end of this file; read that provider's row before its reactive run.

Why this design

Surplus routes each buyer to the cheapest healthy seller for a model, settling per-request in USDC on Base to the seller's wallet. Public endpoints make the market observable, so pricing is reactive, not blind: GET /api/markets/{model} is the live order book - every rival's real price, health, trust flag and remaining cap - and GET /v1/prices gives the per-provider cost basis (what Bankr charges you). The engine prices each offer against the reduced book (see Who actually competes) - undercutting to win routing when someone who can actually serve is cheaper, and probing upward only when you're already the cheapest.

The economics - free credit, so every sale is pure profit. In the default (undercut) mode the compute source is free - Bankr credit (token-launch fees / promo credits), free AWS credits, or free Google Cloud credits - so your_provider_cost ≈ $0 and profit per request = the whole buyer price. There is no cost to defend, no such thing as selling "below cost," and no "burn," "bleed," or "negative edge" - that framing does not apply to free inventory. The only axis is revenue = Σ (volume × price). Winning routing means being cheapest, which trades price for volume: a deeper discount wins more books but keeps fewer USDC per token; a shallower one keeps more per token but wins fewer books. The engine's whole job is to sit at the revenue-maximizing point of that curve. (floor still exists for the rare operator funding a real per-token cost - an out-of-credit Bedrock/Vertex account - who wants a hard price floor; unset by default, because free credit needs none. In floor mode the cost basis C comes from that provider's adapter row: /v1/prices for bankr, config bedrock_prices/vertex_prices for aws/vertex.)

Guardrails are load-bearing because every write is real money in two directions (buyers pay you, you owe the provider):

  • Cost floor (floor) - never list below your true cost.
  • cap_daily_usd on every offer - provider-spend protection against runaway routing.
  • Idempotency - offer IDs live in state; the engine GET /offers first and never creates a duplicate offer for a model it already lists.
  • Fail-closed - offer writes are the final in-run action; a non-2xx aborts the run and reports the true reason.

Auth model

Runtime is Bearer-only. Each provider has its own si_seller_... seller-wallet key - minted once via SIWE and stored as a distinct repo secret (SURPLUS_SELLER_KEY for bankr, SURPLUS_SELLER_AWS_KEY for aws, SURPLUS_SELLER_VERTEX_KEY for vertex). Separate wallets are mandatory: two providers sharing one wallet would clobber each other's offers on the same Surplus account. No private key ever enters CI. Below, {SURPLUS_SELLER_<P>_KEY} denotes the running provider's wallet secret from the adapter table.

Mint it - the dashboard (recommended). Settings → Secrets → the provider's wallet secret → Connect wallet. Your browser wallet (MetaMask / Rabby / Coinbase) signs the SIWE challenge; the key is minted and written to the secret server-side - your private key never leaves the wallet and the key is never shown in the browser. Reconnect rotates it. (Routes: GET /api/surplus/challenge → wallet personal_signPOST /api/surplus/mintsetSecret.)

Fallback - headless / no browser wallet. Run locally, never in Actions (pass the target wallet secret's label):

npm i viem
SELLER_WALLET_PRIVATE_KEY=0x... node skills/compute-resell/bootstrap-siwe.mjs compute-resell
# paste the printed si_seller_... into the dashboard as the provider's SURPLUS_SELLER_*_KEY

All API calls below use ./secretcurl so the Bearer key stays off the command line (the running provider's wallet secret):

./secretcurl -sS -w '\nhttp=%{http_code}\n' --max-time 30 \
  -H "Authorization: Bearer {SURPLUS_SELLER_KEY}" \
  "https://api.surplusintelligence.ai/v1/seller/offers"

Host: https://api.surplusintelligence.ai (call directly; don't follow redirects). Print http=<code> and decide from it - only degrade on a real non-2xx, --max-time timeout, or a 200 with empty body, and log the true reason (http-<code> / timeout / empty). Never write "sandbox"/"expansion blocked".

Config

Provider-name convention (read once). The engine body below is written with the bankr adapter's names as the canonical stand-in: wherever it says COMPUTE_RESELL_CONFIG, memory/state/compute-resell.json, or SURPLUS_SELLER_KEY, read the running provider's equivalent from the Providers adapter table - i.e. AWS_COMPUTE_RESELL_CONFIG + memory/state/aws-compute-resell.json + SURPLUS_SELLER_AWS_KEY when RESELL_PROVIDER=aws, the VERTEX_/vertex- forms when RESELL_PROVIDER=vertex. The helper scripts already resolve this off RESELL_PROVIDER; you resolve it the same way when you run a command by hand. Two more per-provider paths follow the same rule: the topic file memory/topics/compute-resell.mdaws-compute-resell.md / vertex-compute-resell.md, and the earnings ledger memory/compute-earnings.csvaws-compute-earnings.csv / vertex-compute-earnings.csv (bankr keeps the unprefixed names). The knobs, defaults, and mechanics are identical across providers - only the secret/file names and the provider wiring (endpoint, credential, cost basis, denylist in Provider adapters) change.

Zero config to start - each provider runs on safe defaults. To tune a provider, set its optional config secret (COMPUTE_RESELL_CONFIG / AWS_COMPUTE_RESELL_CONFIG / VERTEX_COMPUTE_RESELL_CONFIG) in the dashboard (Skill Keys) to a tiny JSON:

KnobDefaultWhat it does
daily_budget_usd10Total free credit to deploy per day across all this provider's offers. Split evenly across the listed models (n offers x budget/n). Lower it to concentrate on fewer models. Free credit, so it just bounds daily inventory, not loss.
floor(unset -> undercut)Unset (default) -> undercut mode: list just below the cheapest competitor to win routing (pure profit on free credit). Set it only if you fund a real per-token cost: the lowest price you'll list as a multiple of the cost basis C - 1.0 = never below cost, 0.6 = down to 60% of it.
max_discount(unset -> no cap)Discount-rate guardrail: the deepest discount off the direct sticker this provider will list at. 0.80 = never deeper than 80% off (d_in <= 0.80 and d_out <= 0.80), and skip any model whose market already clears deeper (unwinnable at your rate). Unset -> uncapped. See Maximum-discount cap.
discount_adaptivefalseSelf-tune max_discount for revenue. true -> treat max_discount as a starting point and nudge the effective cap <= discount_step per run toward the revenue-max point, within [discount_min, discount_max]: a sold-out offer -> tighten one step (shallower, more USDC/token); a cold run (revenue ~0 or <= 1 winnable book) -> loosen one step (deeper, open more books); else hold. Persists in state (adaptive_discount). Guarded so a tighten never prices out a live earner. See Adaptive discount.
discount_min / discount_max / discount_step0.30 / 0.90 / 0.05Bounds and per-run step for discount_adaptive (ignored when off). discount_max is a real rate wall (never lists deeper).
min_market_volume_usd1.0The selection liquidity gate (the only one): minimum 24h marketplace $-volume (volume_24h / 1e6) a model needs to be listed. Drops dead markets that trade ~$0 before scoring, so the budget concentrates on real liquidity. 0 disables it.
cap_daily_usd(derived)Optional hard per-offer ceiling. Unset -> each offer's cap = the even split max(1, daily_budget_usd / n). Set it to cap any single offer below that share. Allocation is always even.
min_credit_days3Low-credit warning (bankr only): when a real credit balance is read, warn if it is under this many days of runway. Warn-only - never throttles spend. 0 disables.
payout_address(unset)Base address that receives USDC settlement for offers this provider creates. Unset -> the seller wallet. Point bankr at a Bankr wallet and earnings auto-top-up the credits that fund the next sales. Applied at create time only.
denied_models(unset)Hard denylist - never list these (matched by Surplus market id or mapped provider id). Also auto-learned from create/probe access errors. Seed with models the provider account can't serve (see Provider adapters).
model_map(unset)aws/vertex only: map the Surplus market id -> the provider's serve id (and, for vertex, a per-model region). Unmapped models fall back to a one-probe test-list. See Provider adapters.
bedrock_prices / vertex_prices(unset)aws/vertex only, floor mode only: your per-token cost basis C for the model, $/1M tokens. Not read in the default undercut mode. See Provider adapters.

Example secret value: {"daily_budget_usd":50,"max_discount":0.80} - or leave the whole secret unset to take defaults ($10 budget, undercut mode, uncapped discount). Concentrate credit on fewer models by lowering the budget: {"daily_budget_usd":20}. Point earnings back at your credit wallet: {"payout_address":"0xYourBankrWallet"}.

Everything else is automatic: models are discovered from the live market (/v1/prices), ranked by live demand (/api/markets) and listed most-active-first up to the auto-derived offer count, and the engine prices each offer against the current market price - down toward floor to win routing, up toward the reference to capture margin.

Reading COMPUTE_RESELL_CONFIG - read it by name, NEVER via $-expansion. It's injected as a secret, and the Bash permission layer blocks any command whose text contains $COMPUTE_RESELL_CONFIG / ${COMPUTE_RESELL_CONFIG} - so jq <<< "$COMPUTE_RESELL_CONFIG" or echo "$COMPUTE_RESELL_CONFIG" is refused, and the skill then silently falls back to defaults (this is exactly the bug that made a set config look unset). Read it by name through os.environ instead - the command line carries only the literal name, no $-expansion, so it's allowed:

python3 -c "import os,json
c=json.loads((os.environ.get('COMPUTE_RESELL_CONFIG') or '').strip() or '{}')
print(json.dumps({k:c.get(k) for k in ('floor','max_discount','discount_adaptive','discount_min','discount_max','discount_step','daily_budget_usd','min_market_volume_usd','cap_daily_usd','min_credit_days','payout_address','denied_models','model_map','bedrock_prices','vertex_prices')}))"

Take max_discount, daily_budget_usd, floor, etc. from the parsed object; empty/unreadable/invalid JSON → use defaults. Always log the resolved config so a run makes plain whether the override took effect - config: max_discount=0.80 daily_budget=5 vs config: defaults (unset). (printenv COMPUTE_RESELL_CONFIG also works - same no-$ principle - but the Python parse above is the reference.)

State: memory/state/compute-resell.json - auto-written by the skill each run (you never edit this; it's how the pricing loop remembers across runs). Schema:

{
  "version": 8,
  "adaptive_discount": null,
  "earnings_cursor": { "total_earned_usdc": 0, "paid_usdc": 0, "tokens": 0, "at": "<ISO>" },
  "offers": {
    "<offer_id>": {
      "model": "claude-haiku-4.5",
      "cost_in": 1.0, "cost_out": 5.0,
      "market_in": 1.0, "market_out": 5.0,
      "direct_in": 1.2, "direct_out": 6.0,
      "price_in": 1.0, "price_out": 5.0,
      "discount_in": 0.17, "discount_out": 0.17,
      "payout_address": null,
      "low_demand_runs": 0,
      "dead_runs": 0,
      "last_window": { "tokens": 0, "earned_usdc": 0, "at": "<ISO>" },
      "last_move": "init"
    }
  },
  "competitors": {
    "<their_offer_id>": { "model": "claude-haiku-4.5", "obs": 6, "unhealthy": 2, "eff_in": 0.9, "eff_out": 4.5, "last_seen": "<ISO>" }
  },
  "health_incidents_seen": []
}

(cost = provider cost basis; market = surviving clearing price from the /api/markets/{model} order book; direct = direct_*_per_1m, the discount denominator; discount = 1 - price/direct; price = your listed price; low_demand_runs = consecutive runs this offer's model has been below the min_market_volume_usd gate, used by the liquidity-dip debounce; dead_runs = consecutive runs this offer has been healthy:false, used by the dead-offer reap; adaptive_discount = the tuned effective max_discount carried across runs when discount_adaptive is on, else null. All prices per 1M tokens.)

Migration. Older state (version <= 7) may lack or carry now-unused keys. Treat every field as optional: missing direct_*/discount_* -> recompute from the book this run; missing competitors -> start an empty map (the flapper test doesn't fire until it has 6 observations); missing payout_address/low_demand_runs/dead_runs -> null/0; missing adaptive_discount -> null (starts from the config max_discount). Ignore and drop any legacy pool, active_key_env, or per-offer bankr_key_env fields (removed in the single-key / no-pool-learning simplification). Write version: 8 on the way out.

Required secrets

Secrets are grouped by provider; an instance sets only the group(s) it uses. A provider is enabled iff both its wallet and its credential are set (see Providers). Repo variables (not secrets) BEDROCK_BASE_URL, VERTEX_PROJECT_ID, VERTEX_LOCATION complete the aws/vertex wiring (details in Provider adapters).

SecretProviderUsed byPurpose
SURPLUS_SELLER_KEYbankrall modessi_seller_... Bearer wallet key. Mint via the dashboard Connect wallet (or bootstrap-siwe.mjs).
BANKR_LLM_KEYbankrlistingProvider key (bk_...), sent to Surplus only when creating an offer (stored encrypted to call Bankr). Needed to auto-list; repricing doesn't use it. Unset → monitor+reprice only.
COMPUTE_RESELL_CONFIGbankroptionalCompact JSON config override (Config table). Unset → defaults.
SURPLUS_SELLER_AWS_KEYawsall modessi_seller_... Bearer wallet key for the aws Surplus wallet (separate from bankr's).
BEDROCK_API_KEYawslistingBearer key for your OpenAI-compatible Bedrock proxy. Sent to Surplus only on create. Unset → monitor+reprice only.
AWS_COMPUTE_RESELL_CONFIGawsoptionalCompact JSON config override. Unset → defaults.
SURPLUS_SELLER_VERTEX_KEYvertexall modessi_seller_... Bearer wallet key for the vertex Surplus wallet (separate from the others).
VERTEX_SERVICE_ACCOUNT_JSONvertexlistingThe service-account JSON string, passed to Surplus as api_key on create (covers the full Vertex catalog). Primary Vertex credential.
VERTEX_API_KEYvertexlisting (alt)Alternative AIza… Gemini-Developer key (Gemini-only). Used if VERTEX_SERVICE_ACCOUNT_JSON is unset.
VERTEX_COMPUTE_RESELL_CONFIGvertexoptionalCompact JSON config override. Unset → defaults.

If a provider's wallet secret is unset, that provider is simply skipped (not an error). If no provider is enabled (no wallet+credential pair set anywhere): ./notify "compute-resell skipped: no provider configured (need a SURPLUS_SELLER_*_KEY + its credential)" and exit COMPUTE_RESELL_NO_KEY.

Cross-skill claim ledger (never double-list a sibling's model)

This skill's providers (bankrmemory/state/compute-resell.json, awsaws-compute-resell.json, vertexvertex-compute-resell.json, plus any legacy sibling whose state file matches memory/state/*compute-resell.json) all sell on the same Surplus marketplace. Surplus routes /api/markets/{model} to the cheapest healthy offer regardless of which provider backs it, so if two of your providers list the same market id they land in one order book and undercut each other, dragging your own clearing price toward zero. The ledger enforces one owner per market id across all providers (never create duplicate offers). It works identically whether the providers run in one unified skill (this design) or as separate legacy fork skills, since discovery is purely by state-file glob.

Rule: one market id is owned by exactly one fork. Never list a model a sibling already lists.

Discovery is dynamic and zero-config: peers are found by globbing memory/state/*compute-resell.json (minus the running provider's own file), so an added provider is respected automatically and a delisted model frees its claim on the next run. The helper skills/compute-resell/claim_ledger.py reads every peer's committed live offers; python skills/compute-resell/claim_ledger.py <state-skill> memory/state (where <state-skill> is the running provider's state-file basename - compute-resell / aws-compute-resell / vertex-compute-resell) prints {siblings, claimed_models, conflicts}. Run it per-provider in the Preamble right after loading that provider's state, and apply it in two places:

  • R1, exclusion (selection). At the very start of selection (before the min_market_volume_usd liquidity gate, scoring, any test-list, and any create), drop every market id in claimed_models. A sibling-claimed model is never scored, probed, or created. Log each drop: sibling-claimed: deepseek-v3.2 (aws-compute-resell) - skipped.
  • R2, conflict self-heal (reconcile). For any model this fork currently lists that a sibling also lists (a pre-existing double-claim, or the rare simultaneous-run race the staggered cron normally prevents), keep it only if conflicts[model].keep_here is true; otherwise move it to to_delist regardless of score (a hard reap, exempt from hysteresis). Ownership = the holder with the higher last_window.earned_usdc on that model, tie broken by smallest skill name: a pure function of the shared committed state, so every fork computes the same winner and the lineup converges to one owner per model. Log: sibling-conflict: gemini-2.5-pro - vertex-compute-resell keeps (earned $0.18 > $0.00), delisting here.

R3, claim stamp. On every create, write claimed_at: <UTC ISO8601> into the offer's state entry (audit trail + secondary tie-break for future forks). A missing claimed_at on an older offer is treated as epoch (oldest).

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
750
Forks
264
Last commit
Sep 2026

ahel review

  • K6low
    bundled executables the agent is told to run

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Catalog kind
skill
Gateway key
compute-resell
Source
github.com/aeonfun/aeon