Skip to content

feat(skills): skill marketplace v0 — sell + rate skills as a single unit#632

Open
bussyjd wants to merge 3 commits into
mainfrom
feat/skill-marketplace
Open

feat(skills): skill marketplace v0 — sell + rate skills as a single unit#632
bussyjd wants to merge 3 commits into
mainfrom
feat/skill-marketplace

Conversation

@bussyjd

@bussyjd bussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

What

Makes a skill (a SKILL.md + scripts bundle, like the ones embedded in the obol binary) a first-class sellable and ratable unit on the stack, in two modes:

  1. Share mode — sell the bundle bytes. New ServiceOffer type=skill: the gzipped bundle lives in a ConfigMap, the controller validates it (existence, 900 KB cap, sha256 match) and renders a restricted-PSS busybox httpd serving bundle.tar.gz + skill.json behind the standard x402 payment gate at /services/<name>/*.
  2. Service mode — sell the skill's execution: obol sell skill --as-service --agent <a> is thin sugar over the existing type=agent offer path, carrying the skill identity in registration metadata. Zero new controller surface.

Ratings and integrity ride ERC-8004 (live on Base mainnet + Sepolia) rather than a new contract:

  • obol skills calldata set-hashsetMetadata(agentId, "skill.sha256:<name>@<ver>", <hash>) on the IdentityRegistry — binds bundle bytes to the publisher identity.
  • obol skills calldata feedbackgiveFeedback with tag1="asr:skill", tag2="eip155:<chainId>:<registry>:<agentId>:<name>@<ver>" — per-skill ratings under the publisher's agentId, on-chain filterable via getSummary(agentId, raters, tag1, tag2). The tag convention follows the draft ERC-8239 (ethereum/ERCs PR #1704) so we are forward-compatible if it merges; we deliberately do NOT depend on its contracts (unmerged, Sepolia-only).
  • obol skills reputation (summary read w/ --raters whitelist → spam-resistant) and obol skills verify (local bundle sha256 vs on-chain metadata).

All chain writes are operator-submitted calldata. The controller never signs.

Why

The 402 response advertises extra.skill = {name, version, sha256} for free, pre-purchase — a buyer can verify what they will get against the publisher's on-chain hash before paying, and verify the bytes after. That closes the marketplace loop (discover → verify → pay → re-verify → rate) with only the registries already deployed.

Agent self-publish (and its guardrails)

A running agent can sell its own skills: a new namespace-scoped hermes-skill-publish Role grants ConfigMap create/get/update/patch in the agent's own namespace (it already had ServiceOffer CRUD). Two containment layers:

  • A new ValidatingAdmissionPolicy rule: agent ConfigMap writes MUST match *-skill-bundlehermes-config and every other operator-managed ConfigMap are untouchable (test-pinned).
  • The controller refuses skill offers whose upstream isn't the controller-rendered bundle workload (anti-spoof: the advertised hash can never describe someone else's upstream).

Notable implementation details

  • Deterministic packaging (internal/skillpkg): sorted entries, fixed epoch mtimes, cleared uid/gid, normalized modes → reproducible sha256 across machines (test-pinned). Warn-only secret scan before publish.
  • Bundle ConfigMap applied server-side (client-side apply would blow the last-applied annotation cap for bundles >~190 KB).
  • Bundle workload names capped at the 63-char Service/label limit with hash fallback.
  • obol sell delete cleans up the bundle ConfigMap (CLI-created, so no ownerRef GC).
  • Offers persist in the sell resume ledger → survive host reboots via obol sell resume / stack up.
  • This PR also carries internal/erc8004/{reputation,validation}.go (calldata builders + readers for the Reputation/Validation registries) which the CLI commands need — additive, golden-tested selectors.

Validation

  • Full unit suite green (controller render/reconcile, verifier 402 extras, CLI flags, golden calldata, CRD↔Go parity, deterministic-pack tests).
  • Live smoke on a fresh k3d cluster (dev-built images from this branch): sell → condition ladder green → 402 extra.skill byte-exact vs served bundle → VAP denies the agent SA on hermes-config and allows *-skill-bundle → delete cleans everything.
  • Paid end-to-end on Base Sepolia through the public facilitator: agent-signed one-shot x402 payment → HTTP 200, settlement tx 0xb3075516fbbb4c14076b5b94d7db30e2a68ae2c2cf977c58ba1e8ca0715da6d3 (status 0x1), exact ±0.001 USDC balance deltas on buyer and payTo.
  • New flows/flow-19-skill-sale.sh + docs/guides/skill-marketplace.md.

Known v0 limitations

  • Re-publishing a bundle serves stale bytes for the ConfigMap propagation window (~60–120 s); the content-hash pod annotation rolls the server on reconcile.
  • buy.py pay is text-only — the printed purchase instructions steer binary downloads to a binary-safe client (paid skill.json fetch is the documented buy.py path).
  • Per-copy licensing is off-chain; the on-chain layer covers identity, integrity, and reputation.

…dle download + skill-as-a-service)

- ServiceOffer type=skill (spec.skill: name/version/sha256/bundleConfigMap, CEL
  guard); controller validates bundle CM (existence/900KB cap/sha256 match) and
  renders a restricted-PSS busybox httpd bundle server behind the standard
  Middleware+HTTPRoute publish path; anti-spoof: upstream pinned to the
  controller-rendered workload name
- 402 extra.skill {name,version,sha256} surfaced free pre-purchase (additive)
- obol sell skill (--from/--from-embedded, deterministic tar via
  internal/skillpkg, warn-only secret scan, server-side CM apply, resume-ledger
  bundle) + --as-service sugar over type=agent
- obol skills calldata set-hash|feedback (tag1=asr:skill, ERC-8239 PR#1704
  interim tag2), reputation (getSummary + --raters whitelist), verify
  (on-chain sha256 vs local bundle); operator submits, controller never signs
- hermes-skill-publish Role (namespace-scoped CM create/get/update/patch) gated
  by new VAP rule: agent ConfigMap writes must be *-skill-bundle — hermes-config
  untouchable; agent-side publish documented in sell/monetize-guide skills
- flows/flow-19-skill-sale.sh + docs/guides/skill-marketplace.md
- review: high (binary-corrupting buy.py download instructions) fixed in-flow;
  post-review hand fixes: 63-char Service/label cap on workload names, VAP
  ConfigMap guard, sell delete cleans bundle CM, secret-scan errors surfaced,
  doc drift
@OisinKyne

Copy link
Copy Markdown
Contributor

Is this just different UX for obol sell mcp? not really i understand the purpose of this PR (i guess bar the ratings bit)

@bussyjd

bussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

It's not. It sells the bundle bytes with integrity + a publisher-portable identity, vs. sell mcp which proxies a running tool.
Basically selling atomic skill(s) that are being used by a given agent.

@bussyjd

bussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author
  • sell mcp sells RUNTIME: you host a tool, the buyer calls it per-request,
    your API key stays server-side, the buyer never possesses the tool. Renting
    an API.
  • sell skill (share mode) sells the ARTIFACT: the buyer pays once, downloads
    the SKILL.md + scripts bundle, and runs it inside their own agent forever.
    Buying the package, not the API. The bytes are sha256-pinned to the seller's
    on-chain identity, so the buyer verifies integrity before and after paying.
  • sell skill --as-service is the only bit that overlaps sell agent — it's
    sugar over the agent path for selling a skill's execution. You're right that
    part is thin; it's there for completeness.

The user: a seller whose edge isn't GPU but a capability — a good research
skill, a trading workflow, a domain tool. Our whole sell surface today is
inference-by-the-token, which is a commodity: every seller offers the same open
models and competes on price to the floor. Skills are the differentiated,
higher-margin inventory — package your expertise once, sell it portable to
other agents. It moves a seller from "rent my hardware" to "buy my know-how,"
and it's the missing distribution channel for the ~21 skills we already ship
plus anything third parties write (today there's no integrity-preserving way to
hand a skill to another agent).

The --as-service mode was thin sugar over `obol sell agent` (it emitted a
plain type=agent offer with the skill name in registration metadata, no
controller involvement). It duplicated the agent sell path and muddied the
`sell skill` surface, which is about selling bundle BYTES. Removed the flag,
its --agent companion, the runSellSkillAsService/buildSkillServiceOfferManifest
code, and the service-mode test; docs now point at `obol sell agent` for
selling a skill's execution. SHARE mode (paid bundle download) is unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants