Version: 0.2
Status: Production target
Repository: github.com/StellarRoute/WaveFlow
Open-source bounty programs on Stellar today lack a ledger-native, automated payout pipeline tied to verifiable contribution events (e.g. merged GitHub pull requests).
Drips Wave demonstrates a proven model: maintainers define reward pools, contributors earn points from merged PRs, and payouts flow automatically from a synced off-chain pipeline. That model is powerful but not anchored on Stellar/Soroban. Maintainers who want transparent, programmable, wallet-direct rewards must either:
- run manual treasury ops (slow, error-prone, unauditable), or
- rely on centralized payout rails disconnected from Stellar assets.
WaveFlow solves this by implementing the Wave mechanics on-chain: maintainers lock Stellar assets in a Soroban escrow contract; a verified GitHub merge event triggers an authorized gateway attestation; the contract computes reward from a configurable point-to-token ratio and pays the contributor's Stellar wallet immediately.
Core pain: no trust-minimized, automated, Stellar-native bounty escrow with GitHub-verified triggers.
| Persona | Context | Primary need |
|---|---|---|
| Program maintainer | OSS repo owner or ecosystem grant operator funding Wave-style sprints | Lock funds, set point economics, link GitHub repo, monitor payouts without manual transfers |
| Contributor | Developer merging PRs into a registered repo | Register Stellar wallet, earn on merge, see payout status on-chain |
| Gateway operator | StellarRoute-style infra team running the attestation service | Secure webhook ingestion, idempotent attestations, observability |
| Integrator / auditor | Wallet, explorer, or compliance reviewer | Read-only API + on-chain events for program state and payout history |
Assumed context: Users are familiar with Stellar wallets (Freighter, etc.) and GitHub PR workflows. WaveFlow does not replace GitHub; it reacts to merge events. Drips Wave-style sprints assume top-tier open-source contributors implementing and reviewing production-grade code, not scaffold tolerance.
WaveFlow v1 is production-limited (testnet or mainnet-limited launch), not a prototype. Every production path must be:
| Principle | Requirement |
|---|---|
| Security-first | Fail closed on auth, signature, and idempotency violations; no trust in unverified webhook payloads |
| Observable | Structured logs, Prometheus metrics, health/readiness endpoints, correlation IDs on all requests |
| Deployable | Render-compatible services (render.yaml), Docker images, env-driven config, no hardcoded secrets |
| Auditable | Postgres audit trail for webhooks, attestations, and payouts; on-chain events match off-chain records |
| Idempotent | Same PR, webhook delivery, or attestation cannot cause double payout |
| Fail-closed | Insufficient escrow, paused program, invalid contributor, or chain submission failure blocks payout; no silent partial success |
Contributors participating in Drips Wave-style sprints on WaveFlow must:
- Ship small, focused PRs with a clear scope tied to a feature ID (F1–F7) or launch-gate item
- Meet all acceptance criteria in this PRD for the touched feature; partial implementation is not mergeable
- Avoid placeholder implementations in production paths (no
todo!(), simulated tx hashes, or stub RPC clients behind non-dev flags in prod builds) - Use structured error handling (
crates/shared/src/error.rspatterns); propagate context without leaking secrets - Update docs with behavior changes (
docs/,AGENTS.md, runbooks) when API, env vars, or operational behavior changes - Include tests that prove idempotency, auth rejection, and failure modes for changed code paths
Reviewers reject PRs that:
| Violation | Standard |
|---|---|
| Simulated chain submission in production | Gateway must submit real Soroban transactions via SOROBAN_RPC_URL; dry-run or fake hashes allowed only in unit/integration tests |
| Secrets in logs | No webhook secrets, admin keys, or GATEWAY_SECRET_KEY material in log output |
| Backward-unsafe migrations | Postgres migrations under migrations/ must be additive or explicitly documented with rollback steps |
| Unauthenticated mutating API routes | All POST/PUT/PATCH/DELETE on crates/api/src/routes/admin.rs require valid API_ADMIN_KEYS |
| Missing observability on new failure paths | New error branches must increment metrics or emit structured log fields documented in the runbook |
| ID | Goal | Success signal |
|---|---|---|
| G1 | Ledger-native escrow for bounty pools (XLM or Soroban tokens) | Maintainer deposits; balance visible on-chain and mirrored in Postgres |
| G2 | GitHub merge → attestation → payout in one automated loop | Merged PR triggers payout without manual signer intervention |
| G3 | Configurable point-to-reward economics per program | Maintainer sets ratio; contract computes points × ratio |
| G4 | Idempotent, replay-safe attestations | Same PR cannot pay twice (Postgres unique constraint + on-chain ProcessedPr) |
| G5 | Operable gateway + API mirroring StellarRoute backend patterns | Health/readiness checks, structured logs, Postgres audit trail, Prometheus metrics |
| G6 | Production SLOs for gateway availability | 99.5% uptime for webhook ingestion endpoint over 30-day window; documented in maintainer runbook |
| G7 | Complete audit trail for compliance review | Every payout row links to webhook delivery ID, attestation attempt, Soroban tx hash, and ledger sequence |
| G8 | Mainnet-ready contract patterns | TTL extension on deploy, event emission for all state changes, upgrade path documented in docs/ |
These remain out of v1 scope. v1 itself is still production-grade and production-limited (testnet or mainnet-limited launch), not an MVP scaffold.
| ID | Non-goal | Rationale |
|---|---|---|
| NG1 | Full Drips/Wave product parity (leaderboards, social, multi-tenant SaaS UI) | Scope: escrow + payout engine only |
| NG2 | On-chain GitHub light client / ZK proof of merge | Use authorized gateway attestation (oracle model) |
| NG3 | Streaming payments (e.g. continuous DCA) | Immediate discrete payouts per merge event |
| NG4 | Multi-chain or non-Stellar assets | Stellar/Soroban only |
| NG5 | Dispute resolution / clawback governance UI | Future phase; v1 supports maintainer pause only |
| NG6 | Replacing StellarRoute DEX aggregation | Separate product; may share infra patterns only |
Description: Maintainer creates a bounty program linked to a GitHub repo (owner/name), sets point reward ratio, and deposits tokens into escrow.
| Acceptance criterion | Given | When | Then |
|---|---|---|---|
| AC-F1.1 | Maintainer has approved token allowance | create_program + fund called via admin API or CLI |
Escrow balance increases on-chain; ProgramCreated event emitted; Postgres programs row created with matching on_chain_program_id |
| AC-F1.2 | Deposit below minimum | fund with zero amount |
Transaction reverts with InsufficientDeposit; API returns 400 with structured error body |
| AC-F1.3 | Invalid repo string | create_program with malformed repo |
Transaction reverts with InvalidRepo; API validates slug format before chain submission |
| AC-F1.4 | Production deployment | Maintainer funds program on target network | Real Soroban tx hash persisted; no simulated or placeholder hash in Postgres payouts or program audit rows |
| AC-F1.5 | Token contract verification | fund with unverified token address |
Admin API rejects or documents explicit maintainer acknowledgment; address stored for audit |
Description: Contributors map GitHub username to Stellar address for a program.
| Acceptance criterion | Given | When | Then |
|---|---|---|---|
| AC-F2.1 | Unregistered contributor | Maintainer calls authenticated register_contributor |
Mapping stored on-chain and in Postgres contributors; event emitted |
| AC-F2.2 | Duplicate registration same username | Second register with different address | Reverts with ContributorAlreadyRegistered |
| AC-F2.3 | Unregistered contributor at payout | Merge attestation arrives | Gateway rejects before chain tx; audit log records contributor_not_found; metric incremented |
| AC-F2.4 | Invalid Stellar address | Register with malformed address | API returns 400 before chain submission; no partial DB write |
Description: Gateway receives GitHub pull_request closed+merged webhooks, verifies HMAC, resolves contributor, submits on-chain attestation.
| Acceptance criterion | Given | When | Then |
|---|---|---|---|
| AC-F3.1 | Valid webhook for registered repo/program | PR merged to default branch | Gateway submits real record_merge via Soroban RPC; payout executes; tx hash stored |
| AC-F3.2 | Invalid HMAC | Webhook with bad signature | HTTP 401; no chain tx; raw payload persisted in webhook_events with status = rejected |
| AC-F3.3 | PR not merged | closed without merge |
Ignored; no attestation; audit row with status = ignored |
| AC-F3.4 | Duplicate PR number | Replay webhook or duplicate delivery | Gateway or contract rejects; no double payout; HTTP 200 on replay (idempotent ack) after first success |
| AC-F3.5 | Duplicate GitHub delivery ID | Same X-GitHub-Delivery replayed |
Dedup via webhook_events.delivery_id unique constraint; no second chain submission |
| AC-F3.6 | Rate limit exceeded | Burst of webhooks from single IP or repo | HTTP 429 after configured threshold; no chain tx until window resets |
| AC-F3.7 | Production path | Successful attestation | Soroban tx hash is verifiable on target network explorer; not a dry-run or mock hash |
Description: Contract computes payout as points × reward_per_point and transfers tokens to contributor wallet.
| Acceptance criterion | Given | When | Then |
|---|---|---|---|
| AC-F4.1 | Escrow sufficient; contributor registered | record_merge(points=N) |
Contributor receives N × ratio tokens; PayoutExecuted event emitted |
| AC-F4.2 | Escrow insufficient | Payout exceeds balance | Reverts InsufficientEscrow; gateway surfaces alert; Postgres records failed attempt |
| AC-F4.3 | Program paused | Attestation submitted | Reverts ProgramPaused; gateway does not retry without maintainer action |
| AC-F4.4 | Audit completeness | Successful payout | Postgres payouts row includes tx_hash, pr_number, amount, github_username, stellar_address, and timestamp |
Description: Maintainer can pause/resume program, adjust ratio (with bounds), and withdraw unallocated escrow when paused.
| Acceptance criterion | Given | When | Then |
|---|---|---|---|
| AC-F5.1 | Active program | Maintainer calls authenticated pause |
Further attestations rejected on-chain |
| AC-F5.2 | Paused program with remaining balance | Maintainer calls authenticated withdraw_remaining |
Remaining escrow returned to maintainer; event emitted |
| AC-F5.3 | Non-maintainer or missing admin key | Calls admin function via API or chain | Reverts Unauthorized or API 401/403 |
| AC-F5.4 | All mutating admin routes | POST/PUT/PATCH/DELETE on admin API | Require valid X-API-Key matching API_ADMIN_KEYS; unauthenticated requests return 401 |
Description: REST API exposes program status, payout history (from Postgres), health/readiness endpoints, and Prometheus metrics.
| Acceptance criterion | Given | When | Then |
|---|---|---|---|
| AC-F6.1 | Gateway processed merge | GET /api/v1/programs/:id/payouts |
Returns payout record with real tx hash, PR number, amount, and timestamps |
| AC-F6.2 | Service healthy | GET /health |
200 with DB connectivity status |
| AC-F6.3 | Service ready | GET /ready (or readiness section of /health) |
200 only when DB pool and required config are initialized |
| AC-F6.4 | Authenticated admin | POST with invalid API key | 401 |
| AC-F6.5 | Metrics scrape | GET /metrics on gateway and API |
Prometheus text format; includes webhook, attestation, and payout counters |
| AC-F6.6 | Request tracing | Any API request | Response includes correlation/request ID in logs (TraceLayer on API) |
Description: Programs may define a milestone window (ledger sequence or timestamp) with max payout cap.
| Acceptance criterion | Given | When | Then |
|---|---|---|---|
| AC-F7.1 | Milestone cap reached | Additional merge attestation | Reverts MilestoneCapExceeded; gateway records failure; alert threshold documented |
| AC-F7.2 | Milestone active | Payout within cap | Succeeds; milestone spent counter updated on-chain and in Postgres |
| Area | Constraint |
|---|---|
| Smart contracts | Rust, Soroban SDK 21.x, no_std, WASM target; contract at contracts/waveflow-escrow/ |
| Gateway + API | Rust (Tokio, Axum); gateway at crates/gateway/, API at crates/api/; shared types in crates/shared/ |
| Persistence | Postgres for audit/idempotency (migrations/001_init.sql); optional Redis for rate limits |
| Stellar network | Testnet or mainnet-limited launch; mainnet-ready contract patterns (TTL extension, events) required regardless of target network |
| GitHub integration | Webhooks (pull_request events), HMAC-SHA256 verification (crates/gateway/src/webhook.rs), repo slug owner/name |
| Oracle model | Single authorized gateway address per deployment; multisig upgrade path deferred but documented |
| Assets | Native XLM and Soroban token contracts (SEP-41 style transfers) |
| Deployment | Docker Compose locally (docker-compose.yml); Render Blueprint (render.yaml); bind 0.0.0.0:$PORT |
| CI | cargo fmt, clippy -D warnings, contract unit tests (contracts/waveflow-escrow/src/test.rs), gateway integration tests |
| Secrets | DATABASE_URL, GITHUB_WEBHOOK_SECRET, SOROBAN_RPC_URL, GATEWAY_SECRET_KEY, API_ADMIN_KEYS via env; production uses secrets manager, not plain files |
| Idempotency | Unique constraint on (program_id, pr_number) in Postgres; unique delivery_id on webhook_events; on-chain ProcessedPr storage |
| Area | Requirement |
|---|---|
| TLS termination | HTTPS at load balancer or Render edge; no plain HTTP webhook endpoints in production |
| Secrets management | Render sync-false env vars or external secrets manager; rotation procedures documented |
| Structured logging | JSON or key-value logs with request ID, program ID, PR number, delivery ID; no secret values |
| Metrics and alerts | Prometheus /metrics on gateway and API; alert thresholds documented (see docs/security-checklist.md) |
| Render deployment | render.yaml defines gateway, API, and Postgres; health checks on /health |
| Contract TTL | Deploy script (scripts/deploy-contract.sh) extends instance TTL; documented in runbook |
| Upgrade path | Contract upgrade or migration strategy documented before mainnet-limited launch |
| Migrations | Backward-safe, additive changes preferred; breaking migrations require explicit rollout plan |
| Rate limiting | Public webhook and read endpoints rate-limited; configurable via env or middleware (crates/api/src/middleware.rs) |
Environment assumptions:
- Gateway has funded Stellar key for transaction submission fees.
- Maintainers interact via CLI/SDK or future UI; v1 includes production API + contract client examples.
- GitHub delivers webhooks to a public HTTPS endpoint (ngrok acceptable in dev only).
- Production builds set
RUST_LOGand network env vars explicitly; no implicit testnet fallback whenSOROBAN_RPC_URLpoints to mainnet.
All items must pass before a production-limited launch (testnet or mainnet-limited). Tracked as launch gate checklist.
| # | Criterion | Verification |
|---|---|---|
| PR-1 | Real Soroban RPC submission | No simulated hashes in prod; gateway submits via SOROBAN_RPC_URL; tx hashes verifiable on network explorer |
| PR-2 | Webhook HMAC + delivery ID dedup | X-Hub-Signature-256 enforced; X-GitHub-Delivery dedup in webhook_events |
| PR-3 | Postgres audit trail complete | Webhook, attestation attempt, and payout rows linked; no orphan successful payouts |
| PR-4 | Health and readiness endpoints | /health and readiness signal on gateway and API; Render health checks passing |
| PR-5 | Prometheus metrics + alert thresholds | /metrics scraped; thresholds documented (e.g. waveflow_gateway_webhook_failed_total) |
| PR-6 | Security checklist executed | All items in docs/security-checklist.md checked for target environment |
| PR-7 | Maintainer runbook published | Operational procedures in docs/ (deploy, rotate keys, pause program, incident response) |
| PR-8 | Contract deployed and verified | Escrow WASM deployed via scripts/deploy-contract.sh; ESCROW_CONTRACT_ID set; TTL extended |
- Monitor on-chain escrow balance vs. projected payout liability (active contributors × expected merges × ratio)
- Alert maintainer and gateway operator when balance falls below configurable threshold (e.g. 7 days of expected payouts)
- Surface
InsufficientEscrowfailures as high-severity alerts, not only logs
| Parameter | Policy |
|---|---|
| Retriable errors | Transient RPC errors, network timeout, fee bump needed |
| Non-retriable errors | InsufficientEscrow, ProgramPaused, ContributorAlreadyRegistered payout path, invalid contributor |
| Max attempts | 5 attempts with exponential backoff (e.g. 1m, 2m, 4m, 8m, 16m) |
| Double-pay prevention | Check Postgres (program_id, pr_number) and on-chain state before each retry; idempotent ack to GitHub after first on-chain success |
| Dead letter | After max attempts, mark attestation failed in audit table; manual replay requires operator action |
- Generate new Stellar key; fund for fees
- Update contract authorized gateway address (maintainer/admin action)
- Deploy new
GATEWAY_SECRET_KEYto secrets manager - Rolling restart gateway service on Render
- Revoke or drain old key; verify metrics show successful submissions from new key
- Document rotation date in runbook
| Scenario | Action |
|---|---|
| Suspected webhook forgery or key compromise | Pause affected programs on-chain; rotate GITHUB_WEBHOOK_SECRET and GATEWAY_SECRET_KEY; review webhook_events audit |
| Runaway payouts or bug in attestation logic | Maintainer calls pause on affected programs; stop gateway processing via feature flag or service scale-to-zero |
| Webhook queue backlog | Drain or replay from webhook_events after fix; replays must respect delivery ID and PR idempotency |
| Database unavailable | Gateway fails closed (no chain tx without audit write); readiness endpoint returns not ready |
| # | Question | Impact | Status / default for production v1 |
|---|---|---|---|
| OQ1 | Points source: fixed points per merge vs. label/size-based scoring? | Gateway logic + contract args | Decided: fixed 1 point per merged PR in v1 |
| OQ2 | Who registers contributors: maintainer-only vs. self-serve with GitHub OAuth? | API auth design | Decided: maintainer registers via authenticated admin API; self-serve in v2 |
| OQ3 | Gateway decentralization: single operator vs. multisig oracle set? | Contract admin model | Decided: single authorized gateway address; rotation procedure in Section 8 |
| OQ4 | Token allowlist: any Soroban token vs. curated list? | Security / scam risk | Decided: any token; maintainer bears risk; address verification in AC-F1.5 |
| OQ5 | Testnet vs. mainnet launch target? | Deployment config, render.yaml, RPC URLs |
Open: default testnet for first production-limited launch; mainnet-limited requires PR-8 on mainnet and separate runbook |
| OQ6 | Relationship to Drips Wave: complementary tooling vs. official integration? | Product positioning | Decided: independent Stellar-native implementation; no Drips API dependency in v1 |
| OQ7 | Partial payouts when escrow low: fail vs. pay partial? | Contract behavior | Decided: fail closed (revert); alert maintainer per Operational Requirements |
| OQ8 | PR base branch filtering: default branch only vs. configurable? | Webhook filter | Decided: default branch only in v1 |
| OQ9 | Mainnet-limited scope: single program pilot vs. multi-program from day one? | Capacity planning, alerting | Open: recommend single-program pilot until PR-1 through PR-8 validated on mainnet |
| OQ10 | Redis for rate limiting: required at launch or Postgres-only fallback? | Infra complexity | Open: optional Redis; in-memory or Postgres-backed limit acceptable for production-limited v1 if documented |
sequenceDiagram
participant GH as GitHub
participant GW as WaveFlow Gateway
participant DB as Postgres
participant SC as Soroban Escrow
participant W as Contributor Wallet
Note over SC: Maintainer funds escrow
GH->>GW: pull_request merged webhook
GW->>GW: Verify HMAC + delivery ID dedup
GW->>DB: Check pr_number not processed
GW->>SC: record_merge(program, contributor, pr, points)
SC->>W: Token transfer (points × ratio)
SC-->>GW: Tx success + events
GW->>DB: Persist payout audit row