- Create or choose a GitHub issue.
- Decide the MRWK amount using the reference tiers.
- Add acceptance text that explains what counts as useful accepted work.
- Set
max_awardsto the number of separate payouts allowed. Use1for a single-award bounty. - Use
/adminorPOST /api/v1/bountieswith an admin token. Multi-award bounties reservereward_mrwk * max_awards. - Add
mrwk:bountyto the GitHub issue.
- Review the submission and test evidence.
- Confirm the work matches the bounty acceptance text.
- Confirm the labeler is listed in
MERGEWORK_GITHUB_ACCEPTED_LABELERS. - Confirm the PR body links the bounty issue. Use
Bounty #3orRefs #3for multi-award bounties; useCloses #3only when the bounty issue should close after that PR. - Apply
mrwk:acceptedto the PR. - Confirm the webhook records one ledger payment to the PR author.
- Add
mrwk:paidto the PR after the explorer/API shows the proof. - Add
mrwk:paidto the bounty issue only after all awards are exhausted or the bounty is intentionally closed.
To close an open bounty without paying the remaining awards, release the unused reserve:
curl -X POST https://api.mrwk.ltclab.site/api/v1/bounties/<id>/close \
-H "content-type: application/json" \
-H "x-mergework-admin-token: $MERGEWORK_ADMIN_TOKEN" \
-d '{
"reference": "https://github.com/ramimbo/mergework/issues/<issue>#close",
"closed_by": "maintainer-login"
}'If the contributor linked a wallet, the payout goes directly to that mrwk1
address. If not, it goes to github:{login} and can be claimed later after
GitHub OAuth and wallet-linking.
Use the admin-token payout API when the accepted proof is a comment, wallet address, or other non-PR submission:
curl -X POST https://api.mrwk.ltclab.site/api/v1/bounties/<id>/pay \
-H "content-type: application/json" \
-H "x-mergework-admin-token: $MERGEWORK_ADMIN_TOKEN" \
-d '{
"to_account": "mrwk1...",
"submission_url": "https://github.com/ramimbo/mergework/issues/2#issuecomment-...",
"accepted_by": "maintainer-login"
}'to_account must be a registered mrwk1... wallet or github:{login}.
Successful responses include submission_id, ledger_sequence, ledger_url,
proof_hash, and proof_url so operators can reconcile the payment without
scraping the ledger. If the same bounty/submission URL is paid twice, the API
returns 409 with status: "already_paid" and the existing proof links instead
of creating another ledger entry.
Manual payout checklist:
- Verify the public proof and wallet address.
- Pay through
POST /api/v1/bounties/{id}/pay. - Confirm the explorer/API shows the proof.
- Add
mrwk:paidto the paid comment or submission when possible. - Add
mrwk:paidto the bounty issue only after all awards are exhausted or the bounty is intentionally closed.
Do not apply mrwk:accepted to maintainer-authored bounty issues for payment;
those issues require the manual payout path.
Use the admin-token webhook events API to inspect recent delivery outcomes before retrying labels or making manual payouts:
curl -s "https://api.mrwk.ltclab.site/api/v1/admin/webhook-events?status=missing_submitter" \
-H "x-mergework-admin-token: $MERGEWORK_ADMIN_TOKEN"The response includes delivery ID, GitHub event type, payload hash, processed
status, and timestamp. This is useful for confirming duplicate deliveries,
missing submitters, exhausted bounties, and already-paid submissions without
guessing from GitHub labels alone.
Use limit to control the number of delivery rows returned (1 to 200,
default 50), for example:
/api/v1/admin/webhook-events?status=missing_submitter&limit=100.
Use the queue-health script before accepting busy bounty rounds:
python scripts/pr_queue_health.py --repo ramimbo/mergework --format textFor a report that can be pasted directly into a GitHub issue, PR comment, or payment-batch note, use Markdown output:
python scripts/pr_queue_health.py --repo ramimbo/mergework --format markdownLive mode requires an authenticated GitHub CLI with access to the repository.
The command only reads PRs and issues; it does not close PRs, label issues, or
post comments. It reports missing bounty references, closed or exhausted bounty
references, dirty or unknown merge state, mrwk:needs-info, and likely duplicate
PR scope within the same bounty issue.
For offline checks, save fixture data and run:
python scripts/pr_queue_health.py --input queue.json --format json --fail-on-issues--fail-on-issues exits nonzero when queue-health problems are found, which lets
maintainers add the check to local release or payout workflows without requiring
live GitHub access.
- Confirm the webhook or admin API records one ledger payment for that award.
- Confirm the proof pays the intended contributor account.
- Add the paid bounty row to docs/paid-bounties.md and GitHub Discussions #16.
Production GitHub OAuth is configured for https://mrwk.ltclab.site.
Contributors use /me to sign in, link wallets, and claim older GitHub ledger
balances. If the GitHub app is rotated later, update deployment secrets outside
the repository and restart Docker Compose.
- Ask for concrete missing evidence with
mrwk:needs-info. - Use
mrwk:rejectedonly when the submission is clearly not acceptable. - Keep public comments short and specific.
- For security reports, keep private details out of public comments and proofs.
- Database:
/srv/mergework/data/mergework.sqlite3. - Backups:
/srv/mergework/backups. - Health check:
GET /health. - Logs:
docker compose logs -f app caddy.
Generate MERGEWORK_GITHUB_WEBHOOK_SECRET, MERGEWORK_ADMIN_TOKEN, and
MERGEWORK_COOKIE_SECRET with at least 32 random characters. Keep them outside
the repository.
Run the deploy gate before posting a bounty:
docker compose run --rm app python scripts/check_deploy_ready.pyRun a staging webhook payout dry run against a staging host:
MERGEWORK_STAGING_BASE_URL=https://staging.mrwk.example.test \
MERGEWORK_DRY_RUN_REPO=ramimbo/mergework \
docker compose run --rm app python scripts/staging_webhook_dry_run.py