A protocol for AI agents to hire humans for tasks they cannot do themselves — with the contract frozen up-front and the swap of money-for-result made atomic, so neither side can ghost the other.
off-chain · payment-rail-agnostic · zero registration · reference implementation in Python
agent ─signed envelope─▶ ┌──────────────────┐ worker (browser)
│ Protocol Service │ ◀─── HTTP ─── Marketplace Web
│ · Broker │
│ · Escrow adapter │
└──────────────────┘
(one source of truth · single REST contract)
This is the reference implementation. The protocol's whole contribution is two
small promises locked together — see docs/superpowers/specs/2026-05-19-open-human-task-protocol-design.md.
Autonomous AI agents increasingly hit work they structurally cannot do: pass KYC, be physically present, sign in person, run a real-world errand. They need humans. But the existing marketplace shape of this idea is uncredible: workers ghost requesters after authorization, requesters extract value before payment. The empirical failure is documented (arXiv 2602.19514).
Two layers are unsolved and have no standard:
- verification of off-chain human work — "did the human actually, correctly, do it?"
- identity / accountability binding — "who is on the hook on each side?"
Payment and escrow are, by contrast, mature commodities. OHTP doesn't reinvent those. Its entire contribution is a protocol that freezes what counts as done and who is accountable before any work begins, and structures settlement so neither side can ghost the other.
The agent posts a Task Envelope — a JSON document, signed with an Ed25519 key the SDK manages invisibly. The envelope fixes, before any work begins:
principal— who is accountable (an ephemeral identity by default)task— what the human must do, in plain wordsacceptance— a typed evidence schema + a machine-checkable predicatebounty— amount + settlement railsafety— a deny-list (e.g. no OTP relay)
A Broker drives a state machine — POSTED → LOCKED → SUBMITTED → VERIFIED → SETTLED
(plus REJECTED / FAILED branches) — and enforces four anti-ghost invariants:
- Escrow locks at CLAIM, not at submission.
- The agent cannot see the deliverable until
SETTLED. - At
VERIFIED, deliverable-to-agent and payout-to-worker are released atomically by the escrow adapter — neither side has a "grab and run" window. - No state path reaches
SETTLEDwithout aPASSverdict.
These are property-tested, not just asserted in prose.
Settlement is a swappable adapter (lock / release / refund / status). v1 ships
two implementations: ManualLedger (a local JSON ledger, offline default) and
StripeSettlement (test-mode PaymentIntent with capture_method="manual").
There is no on-chain settlement, no account abstraction, no smart-contract
dependency — that slot is reserved for the future, deliberately unimplemented.
For the full walkthrough in plain language, open ohtp-how-it-works.html.
v1 / alpha · reference implementation. Not for production. Locally runnable, end-to-end, with real (test-mode) money rails. 77 tests on every commit, plus a gated Stripe integration test.
Deliberately deferred (named honestly in the spec, not pretended-done):
EXPIREDtimeout enforcement- Appeal window
- Stripe Connect real worker payouts
- An actual crypto/on-chain settlement adapter
- Worker-submit authentication (single-tenant local Marketplace makes this acceptable for v1)
See spec §9 for full deferral notes.
Requires Python 3.11+.
git clone https://github.com/SolvoHQ/ohtp.git
cd ohtp
python3 -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]" # base + test tooling
# Optional Stripe extra (only needed for the Stripe adapter path):
# pip install -e ".[dev,stripe]"Two services + a tiny agent CLI. One command brings them up:
scripts/dev.sh
# Protocol Service -> http://localhost:8000
# Marketplace (human) -> http://localhost:8001In a second terminal, an agent posts a task:
OHTP_PROTOCOL_URL=http://localhost:8000 \
python -m ohtp.agent_client post '{
"id": "blk-1",
"summary": "Photograph the permit at 200 5th Ave.",
"evidence": [{"name": "photo", "kind": "geo_photo"}],
"predicate": "all_present",
"bounty_usd": 25.0,
"created_at": "2026-05-20T00:00:00Z",
"expiry": "2999-01-01T00:00:00Z",
"deadline": "2999-01-01T00:00:00Z"
}'Then open http://localhost:8001 in a browser, claim the task, fill in the
evidence and a deliverable, hit Submit. The agent CLI returns when the task
reaches a terminal state.
Settlement defaults to ManualLedger. To use Stripe test mode instead:
export OHTP_SETTLEMENT=stripe
export OHTP_STRIPE_KEY=sk_test_... # your Stripe test secret
scripts/dev.shDocker:
docker compose -f docker/docker-compose.yml upThe agent entry is a Claude Code Skill at skills/ohtp/SKILL.md. Drop it into
any Claude Code agent (Solvo or otherwise) and an agent will use OHTP when it
hits a human-blocker. No keys, no registration — the SDK builds and manages an
invisible Ed25519 keypair on first use.
ohtp/
crypto.py # canonical signing (Ed25519)
envelope.py # Task Envelope model + sign/verify
evidence.py # typed evidence + bundle validation
predicate.py # deterministic predicate evaluator
settlement.py # SettlementAdapter ABC + ManualLedger
settlement_stripe.py # Stripe test-mode adapter (offline-safe seam)
broker.py # state machine + anti-ghost invariants
server.py # FastAPI REST surface
protocol_service.py # env-wired standalone app + entrypoint
marketplace/ # human-facing web app (pure HTTP consumer)
agent_client.py # agent SDK + CLI (pure HTTP consumer)
mcp_server.py # MCP-style tool surface
solvo_adapter.py # Solvo "blocker" -> signed envelope
skills/ohtp/SKILL.md # the agent-facing Skill
scripts/dev.sh # no-docker local launcher
docker/ # Dockerfile + docker-compose.yml
docs/superpowers/
specs/ # design specs (v0, v1)
plans/ # implementation plans (v0, v1)
tests/ # offline test suite + e2e
ohtp-pitch.html # slide deck: positioning + what was built
ohtp-how-it-works.html # slide deck: plain-language mechanics + flows
.venv/bin/pytest -q
# 77 passed, 1 skipped
# (the 1 skipped is the gated Stripe integration test; set
# STRIPE_TEST_KEY=sk_test_... in your env to run it too.)Every commit on master runs the full offline suite. The Stripe adapter has
unit tests with an injected fake (no network) plus an opt-in integration test
against real Stripe test mode.
The spec and the per-task implementation plans live in the repo:
- v0 spec —
docs/superpowers/specs/2026-05-19-open-human-task-protocol-design.md - v1 spec —
docs/superpowers/specs/2026-05-19-ohtp-v1-design.md - v0 plan —
docs/superpowers/plans/2026-05-19-ohtp-v0.md - v1 plan —
docs/superpowers/plans/2026-05-19-ohtp-v1.md
The HTML decks (ohtp-pitch.html, ohtp-how-it-works.html) are open-in-browser
explainers — the second one is a plain-language walkthrough of every mechanism
plus an Agent-side and a Worker-side flow.
This is an early reference implementation. Issues and PRs welcome; the simplest way to chat about a change is to open an issue describing the use case first.
The codebase follows TDD throughout; tests come with every change. Run
.venv/bin/pytest -q before opening a PR.
Apache License 2.0 — see LICENSE.