Skip to content

feat: add bounty board API endpoints (Phase 1)#2

Closed
secret-mars wants to merge 1 commit intoaibtcdev:mainfrom
secret-mars:feat/bounty-board-api
Closed

feat: add bounty board API endpoints (Phase 1)#2
secret-mars wants to merge 1 commit intoaibtcdev:mainfrom
secret-mars:feat/bounty-board-api

Conversation

@secret-mars
Copy link

Summary

This PR adds Phase 1 of a bounty marketplace to the agent-news platform, following the existing KV-backed architecture and coding patterns exactly.

New endpoints

Method Path Description
GET /api/bounties List bounties with filters
POST /api/bounties Create a bounty (free, BIP-322 auth)
GET /api/bounties/stats Aggregate counts and sats by status
GET /api/bounties/:id Single bounty detail with claims array

What follows existing patterns

  • Storage: KV only via SIGNAL_KV binding (same as classifieds, signals)
  • Auth: BIP-322 signature validation via validateSignatureFormat() from _shared.js
  • Rate limiting: checkIPRateLimit() — 5 bounty creations per hour per IP
  • CORS: Uses the shared CORS, json(), err(), options() helpers throughout
  • ID generation: Same time-based random ID pattern as classifieds (c_${timestamp}_${random})
  • Index pattern: bounties:index → array of IDs, bounty:{id} → object (mirrors classifieds:index)
  • Creator index: bounties:creator:{btcAddr} (mirrors classifieds:agent:{addr})
  • Beat index: bounties:beat:{slug} for beat-scoped queries
  • Claims: Stored separately as bounty:{id}:claims for future claim workflow (Phase 2)

No payment required for creation

Unlike classifieds (5000 sats x402), bounty creation is free. The agent posting a bounty is committing their own funds off-platform, so no platform payment gate is needed.

Signed message format: BOUNTY|create|{btcAddress}|{timestamp}

Bounty object schema (KV)

{
  "id": "lp4x8k2z-abc123-def4",
  "creatorBtc": "bc1q...",
  "creatorName": "Agent Name",
  "title": "Build X",
  "description": "Full description...",
  "amountSats": 10000,
  "tags": ["api", "cloudflare"],
  "skills": ["hono", "workers-kv"],
  "beatSlug": "dao-watch",
  "status": "open",
  "deadline": "2026-03-25T00:00:00Z",
  "claimCount": 0,
  "createdAt": "2026-02-27T16:00:00Z",
  "updatedAt": "2026-02-27T16:00:00Z"
}

Files changed

  • functions/api/bounties.js — GET list + POST create
  • functions/api/bounties/[id].js — GET single bounty with claims
  • functions/api/bounties/stats.js — GET aggregate stats
  • functions/api/_shared.js — adds BOUNTY_MAX_TITLE, BOUNTY_MAX_DESCRIPTION, BOUNTY_MAX_TAGS, BOUNTY_STATUSES constants
  • functions/api/index.js — adds bounty endpoints to manifest and quickstart

Phase 2 (not in this PR)

  • PATCH /api/bounties/:id/claim — agents claim a bounty
  • PATCH /api/bounties/:id/submit — submit work for review
  • PATCH /api/bounties/:id/approve — creator approves and marks paid

Bounty reference: https://bounty.drx4.xyz/bounties/0c71091f-9387-4ae3-afed-c6e039eaa130

Test plan

  • GET /api/bounties returns { bounties: [], total: 0, offset: 0, limit: 20 } on empty KV
  • POST /api/bounties with valid body + signature returns 201 { ok: true, bounty: {...} }
  • POST /api/bounties missing required fields returns 400 with descriptive error
  • POST /api/bounties with amountSats < 1000 returns 400
  • POST /api/bounties with timestamp older than 5 minutes returns 400
  • POST /api/bounties 6th time from same IP within an hour returns 429
  • GET /api/bounties?status=open filters correctly
  • GET /api/bounties?sort=amount_high returns highest sats first
  • GET /api/bounties?skills=hono,workers-kv returns any-match results
  • GET /api/bounties/stats returns correct counts after creates
  • GET /api/bounties/:id returns bounty with claims: []
  • GET /api/bounties/nonexistent returns 404

🤖 Generated with Claude Code

Implements a KV-backed bounty marketplace for the agent-news platform,
following existing patterns from classifieds and signals exactly.

New endpoints:
- GET  /api/bounties       — list with ?status, ?beat, ?skills, ?sort, ?limit, ?offset filters
- POST /api/bounties       — create a bounty (free, BIP-322 signature auth, 5/hr IP rate limit)
- GET  /api/bounties/stats — aggregate counts and sats totals per status
- GET  /api/bounties/:id   — single bounty detail with claims array

New files:
- functions/api/bounties.js       — list + create handlers
- functions/api/bounties/[id].js  — single bounty detail
- functions/api/bounties/stats.js — aggregate stats

Updated files:
- functions/api/_shared.js — adds BOUNTY_MAX_TITLE, BOUNTY_MAX_DESCRIPTION,
                             BOUNTY_MAX_TAGS, BOUNTY_STATUSES constants
- functions/api/index.js  — registers bounty endpoints in manifest + quickstart

KV key layout:
  bounties:index              → [] of ids (newest first)
  bounty:{id}                 → bounty object
  bounty:{id}:claims          → [] of claim objects
  bounties:creator:{btcAddr}  → [] of ids for that creator
  bounties:beat:{slug}        → [] of ids for that beat

Bounty creation is free (no x402). Signed message format:
  BOUNTY|create|{btcAddress}|{timestamp}

Bounty #20 reference: https://bounty.drx4.xyz/bounties/0c71091f-9387-4ae3-afed-c6e039eaa130

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@pbtc21 pbtc21 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean Phase 1 — follows existing KV patterns exactly. Good validation, rate limiting, indexing.

Minor notes for Phase 2:

  • BOUNTY_MAX_TAGS constant (500) is defined in _shared.js but unused — tags validated individually instead
  • List + stats endpoints do N+1 KV reads (one per bounty). Fine at current scale, consider pre-computed stats or batch reads when volume grows
  • Signature is format-checked only (no BIP-322 crypto verify) — matches classifieds pattern, acceptable for MVP

Ship it. 🤝

@secret-mars
Copy link
Author

Closing — PR #12 (v2 rewrite) changes the architecture from Pages Functions to Hono + TypeScript Worker with Durable Object SQLite. Will rewrite the bounty board integration in TypeScript to match the new architecture and open a new PR.

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