Run an OpenAffiliate program, and join other merchants' programs from the same dashboard - #254
Merged
Merged
Conversation
… the same dashboard CrawlProof is the reference implementation of OpenAffiliate (logicsrc.com/docs/openaffiliate): one file a merchant serves about the commission it pays, and four calls that let a person or an agent earn it with no network in the money. The program we run: /.well-known/openaffiliate.json from one constants file (30% of a credits purchase within 30 days of a click, 30-day hold, USDC on Polygon weekly from $10, open approval, self-purchases refused). A navigation carrying ?oa=<code> goes through /api/affiliate/v1/click, which records the click, sets the cookie and strips the parameter; an image, frame or script carrying it sets nothing. Sign-in and invoice creation bind the user to the affiliate; purchase completion records the conversion, pending, idempotent on the purchase; the hourly cron approves past the hold (or reverses refunded ones with a reason), delivers signed webhooks, pays on schedule through CoinPay with debit-first/put-back-on-failure RPCs. Join (public, with an OpenProfile.md), ledger (oa_ token, crp_ token or session), me, token rotation, payout, jwks. Every one of our users gets a membership on first use and an OpenProfile at /affiliate/u/<code>/openprofile.md, which is what they join other merchants with: a directory of descriptors read from each merchant's own origin, joins that hold the merchant's token for the user, ledgers synced daily and on demand, inbound webhooks. Surfaces: /dashboard/affiliate (link, balances, conversions, payouts, joined programs, directory), /affiliate, /affiliate/programs, /affiliate/terms, `crawlproof affiliate link|ledger|pay|payout|programs|join|joined`, seven MCP tools, docs/affiliate.md. Migration 20260913120000_openaffiliate.sql. The 2026-06 referral tables stay; nothing ever wrote a commission there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDEiDss9RWYibtmxSk5Gr2
ThreatCrush Security Scan39 finding(s) HIGH/CRITICAL: 2 | MEDIUM: 28 | LOW: 9
Snippets are redacted; ThreatCrush never prints matched credential material. |
… CLI/MCP parity CodeQL flagged the four things in this feature that were mine: fetching a URL somebody typed (a merchant's descriptor, a profile, a join endpoint, a ledger), one regex-in-a-message line, and two cookie readers that built a RegExp from a variable. lib/affiliate/ssrf.ts now fronts every outbound request: http(s) on a default port only, no loopback / link-local / private names, every resolved address public, redirects followed by hand with each hop re-checked and never on a POST. The cookie readers split the header instead. CLI gains `affiliate webhook` and `affiliate token --yes`; MCP gains affiliate_set_webhook and affiliate_token, so every dashboard action has an API, CLI and MCP twin. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDEiDss9RWYibtmxSk5Gr2
…ier regex Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDEiDss9RWYibtmxSk5Gr2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CrawlProof is the reference implementation of OpenAffiliate (spec: profullstack/logicsrc#166,
logicsrc.com/docs/openaffiliate): one file a merchant serves about the commission it pays, and four calls that let a person or an agent earn it, with no network in the money.The program we run
/.well-known/openaffiliate.jsonfrom one constants file,lib/affiliate/program.ts: 30% of a credits purchase within 30 days of a click, 30-day hold, USDC on Polygon weekly from $10, open approval, self-purchases refused. (Not the 60% the 2026-06 referral header promised: a credits pack pays publishers 1.4c of every 2c click, so 60% would be paid twice over.)proxy.tssends a navigation carrying?oa=<code>through/api/affiliate/v1/click, which records the click, sets theoacookie and redirects back without the parameter. An image, frame, script or prefetch carrying it sets nothing (Sec-Fetch-Dest), which is the whole cookie-stuffing defence.app/auth/callback) and invoice creation bind the user to the affiliate, last touch inside the window, never the affiliate's own account.completePurchaseand the CoinPay webhook record it pending, idempotent on the purchase id,held_until30 days out./api/cron/affiliate, hourly at :23): approve past the hold or reverse refunded ones with a reason (a reversal without a reason is a check constraint violation), deliver Ed25519-signed webhooks with backoff, pay on schedule, re-read stale merchants and ledgers.affiliate_request_payoutflips approved conversions to paid under a row lock, thencreateCryptoPayout; a failed send callsaffiliate_fail_payoutand puts them back. Probed live in a rolled-back transaction: pays only the approved rows, failure path restores them.The affiliate side
Every user gets a membership on first use and an OpenProfile.md at
/affiliate/u/<code>/openprofile.md, which is what they join other merchants with./api/affiliate/v1/programsis a directory read from each merchant's own/.well-known/openaffiliate.json(verified above claimed),programs/joinjoins on the user's behalf and holds the merchant's token for them,joinedshows every ledger on one page,events/<join>takes the merchant's webhooks.Surfaces
/dashboard/affiliate,/affiliate,/affiliate/programs,/affiliate/terms,crawlproof affiliate link|ledger|pay|payout|programs|join|joined, seven MCP tools (affiliate_*),docs/affiliate.md. Three credentials reach the API:oa_ledger token (the spec's),crp_API token, session.Migration
supabase/migrations/20260913120000_openaffiliate.sql: already applied to prod via the Supabase MCP (8 tables, 2 RPCs, croncrawlproof-affiliate). Before: 0 affiliate tables / functions / cron jobs. The cron 404s harmlessly until this deploys. Optional envOPENAFFILIATE_SIGNING_KEY(Ed25519 seed) signs webhooks; unset means unsigned, which the spec allows.Gate
tsc --noEmitclean,vitest run176 files / 2262 tests green (4 new files: spec parser, commission math, links, join parsing, OpenProfile reading, cookie + navigation check, webhook sign/verify, CLI),next buildexit 0 with all 20 new routes in the tree.🤖 Generated with Claude Code
https://claude.ai/code/session_01CDEiDss9RWYibtmxSk5Gr2