ReLoop Rewards is an open-source recycling-incentive platform for recording verified collection events and distributing immediate RUSD micro-rewards through the Fiber Network. A recycler or sponsor defines a bounded campaign, an authorized operator verifies collection at a known site, and a participant receives a reward after the event passes the campaign rules.
The first beta is intentionally narrow: one partner, one collection site, one material type, 50 invited participants, and at least 200 accepted collection events. It is not a national recycling marketplace, a new token, a carbon-credit registry, an automated reverse-vending machine, or a system that claims software can prove physical recycling without an operator and partner process.
The implementation must prove the following product claim:
A sponsor can publish bounded collection rules, an authorized operator can verify a participant event, the system can reject duplicate and ineligible claims, an accepted event can create exactly one RUSD Fiber reward, and the resulting recovery and payment record can be audited against a CKB commitment.
The beta is complete only when it demonstrates:
- campaign creation with material, site, reward, limits, and budget rules;
- participant onboarding with a QR identity;
- operator authorization for the selected collection site;
- item-count and weight-based event recording;
- optional evidence photo capture with explicit consent;
- deterministic duplicate, quota, campaign, and operator checks;
- exactly-once payout reservation for every accepted event;
- real RUSD testnet payment through a pinned Fiber integration;
- participant payout status and payment proof;
- sponsor recovery, rejection, and payout dashboards;
- privacy-filtered audit export;
- CKB commitment for campaign rules and daily collection records;
- automated tests, deployment documentation, pilot evidence, and reconciliation reporting.
| Decision | Beta choice | Reason |
|---|---|---|
| Initial pilot | One recycling partner and one collection site | Keeps physical verification and support manageable |
| Material | One selected material, for example PET bottles | Makes reward and reconciliation rules unambiguous |
| Participants | 50 invited participants | Allows controlled identity and abuse monitoring |
| Target activity | At least 200 accepted collection events | Produces enough evidence for payout and reconciliation testing |
| Operator model | Authorized site operator records and approves events | Software does not pretend to verify physical activity alone |
| Participant identity | QR-based participant identifier | Works on low-cost phones without requiring a new wallet |
| Reward asset | Testnet RUSD | Stable unit for understandable pilot accounting |
| Settlement | Fiber testnet payment after operator approval | Demonstrates immediate micro-reward behavior |
| Campaign scope | One site, one material, fixed campaign window | Prevents unbounded claims in the beta |
| Evidence photo | Optional, consent-based, private by default | Supports review without publishing personal imagery |
| On-chain data | Public hashes and campaign metadata only | Keeps personal data and photos off-chain |
| Mainnet | Out of scope | Requires separate custody, legal, liquidity, and operational review |
| New asset | Out of scope | RUSD is sufficient for the proof |
| Carbon credits | Out of scope | Collection records are not environmental certificates |
| Automation | Operator-assisted verification | No reverse-vending machine integration is required |
Sponsor creates campaign rules and budget
|
Operator is assigned to the campaign site
|
Participant claims an invite and receives a QR identity
|
Participant presents QR code at the collection site
|
Operator records material, count or weight, time, station, and optional photo
|
Validation checks campaign rules, operator authority, limits, and duplicates
|
Accepted event reserves one payout exactly once
|
Fiber worker validates invoice and pays RUSD
|
Participant sees payment status and proof
|
Sponsor sees recovered totals, rejected events, and payout totals
|
Daily accepted records and payout summary are committed to CKB
|
Sponsor downloads a privacy-filtered reconciliation report
The public beta must describe its trust model directly.
- The operator is responsible for accepting or rejecting the physical collection event.
- The platform records who approved the event, where it was recorded, and when it was recorded.
- The platform can detect duplicate, over-limit, and inconsistent claims but cannot prove that every physical item existed.
- Sponsor and partner procedures remain part of the trust model.
- A photo is supporting evidence, not an automatic truth oracle.
- A server-side Fiber payer is used for the sponsored testnet pilot.
- Browser and participant clients never receive payer credentials or private keys.
- The worker reserves a payout before calling a spending RPC.
- Ambiguous payment results are reconciled before retrying.
- All reward amounts use integer base units, never floating-point values.
- Testnet payments have no mainnet monetary value.
- Participant identity data is separated from public recovery exports.
- Public exports contain participant pseudonyms or stable redacted IDs, not names or phone numbers.
- Exact home locations and unrelated movement data are never collected.
- Photos are private review evidence and are not included in public CKB commitments.
- Photo retention and deletion rules are visible to participants and operators.
- Logs redact QR secrets, payment credentials, private endpoints, and evidence URLs.
- CKB stores campaign and collection-record commitments, not raw personal data.
- The committed content is canonicalized before hashing.
- A commitment proves that a published record set matched a hash at a given time.
- A commitment does not prove that a physical event was honest or that a photo is authentic.
Participant Web App / Mobile Web
- invite claim
- QR identity
- campaign instructions
- event history
- payout status
|
| HTTPS
v
Application API
- authentication and roles
- campaign rules
- participant profiles
- operator event intake
- validation decisions
- payout records
- exports and audit queries
|
+--------------------+
| |
v v
PostgreSQL Redis / Worker Queue
- users - payout jobs
- campaigns - commitment jobs
- events - export jobs
- payouts - cleanup jobs
- commitments - reconciliation jobs
| |
| v
| Fiber Settlement Service
| - invoice validation
| - send_payment
| - payment lookup
| - idempotency
|
v
Sponsor and Operator Web App
- campaign management
- event review
- payout monitoring
- reconciliation reports
CKB Commitment Worker
- campaign rules hash
- daily recovery-record root
- payout summary hash
- Browser code never receives Fiber payer credentials.
- Only the settlement worker may call Fiber spending methods.
- Every event has a stable idempotency key.
- An accepted event can have at most one payout reservation.
- A payout is reserved before any payment request is sent.
- An ambiguous payment is not treated as failed until reconciled.
- Campaign rules become immutable after activation.
- Public exports are generated from accepted records and explicit privacy transforms.
- CKB commitment data contains hashes and public metadata only.
- Operator authorization is checked on the server, not only in the UI.
- All state transitions are recorded as audit events.
| Layer | Technology |
|---|---|
| Participant and sponsor app | Next.js App Router + TypeScript |
| Styling | Tailwind CSS or the selected project design system |
| API | Fastify or Next.js route handlers with service boundaries |
| Database | PostgreSQL + Prisma |
| Jobs | Redis + BullMQ |
| Validation | Zod schemas and deterministic validation services |
| Fiber integration | Versioned JSON-RPC adapter pinned to a tested FNN release |
| CKB integration | CCC transaction builder and cell reader |
| QR codes | Standard QR encoder and scanner library |
| Object storage | S3-compatible private bucket for evidence photos |
| Tests | Vitest, integration tests, and Playwright |
| Local deployment | Docker Compose |
| CI | GitHub Actions |
The exact Fiber node version, RUSD asset ID, CKB network, storage provider, and deployment settings must be pinned during the validation work. No production Compose file may use latest.
ckb-reloop-rewards/
implementation.md
README.md
.env.example
package.json
pnpm-workspace.yaml
docker-compose.yml
apps/
web/
app/
participant/
operator/
sponsor/
admin/
api/
components/
lib/
tests/
worker/
src/
payouts/
commitments/
exports/
cleanup/
reconciliation/
api/
src/
auth/
campaigns/
participants/
operators/
events/
validation/
payouts/
exports/
commitments/
audit/
packages/
contracts/
src/
campaign.ts
participant.ts
collection-event.ts
payout.ts
commitment.ts
states.ts
database/
prisma/
schema.prisma
src/
validation-engine/
src/
rules.ts
duplicate.ts
quotas.ts
decisions.ts
fiber-rpc/
src/
adapter.ts
transport.ts
invoices.ts
payments.ts
reconciliation.ts
ckb-commitment/
src/
canonical.ts
merkle.ts
transaction.ts
verify.ts
qr-identity/
src/
payload.ts
signing.ts
verification.ts
export/
src/
recovery.ts
payments.ts
reconciliation.ts
redaction.ts
testkit/
src/
fake-fiber.ts
fake-events.ts
fixtures.ts
infrastructure/
fiber/
payer/
receiver/
postgres/
redis/
storage/
ckb/
scripts/
seed-pilot.ts
fiber-smoke-test.ts
commit-campaign.ts
commit-daily-records.ts
verify-commitment.ts
reconcile-pilot.ts
docs/
architecture.md
privacy.md
data-retention.md
validation-policy.md
fiber-compatibility.md
deployment.md
pilot-runbook.md
threat-model.md
partner-guide.md
idrole: participant, operator, sponsor, reviewer, adminemailor verified contact reference where requiredstatus: invited, active, suspended, deletedcreatedAtupdatedAt
idnamepartnerReferencestatuscreatedAtupdatedAt
idorganizationIdnamepublicAddressLabeltimezoneoperatorInstructionsstatus
idorganizationIdsiteIdnamematerialCodematerialLabelmeasurementMode: count or weightrewardPerUnitweightUnitwhen applicableminimumUnitmaximumUnitparticipantLimiteventLimitbudgetBaseUnitsstartAtendAtstatusrulesVersionrulesHashcreatedByactivatedAtclosedAt
iduserIdpublicParticipantCodeqrSecretHashpayoutReceiverReferenceconsentVersionconsentAcceptedAtstatus
idcampaignIdsiteIdoperatorUserIdstatusvalidFromvalidUntil
idcampaignIdsiteIdparticipantIdoperatorUserIdeventKeymaterialCodequantitymeasurementUnitrecordedAtstationReferenceevidenceObjectKeyevidenceConsentstatus: received, accepted, rejected, needs_review, cancelleddecisionReasonCodeacceptedAtrejectedAtcreatedAt
ideventIdruleCodeseveritydetailsRedactedcreatedAt
ideventIdparticipantIdamountBaseUnitsassetIdidempotencyKeystatus: reserved, invoice_created, submitted, confirming, paid, failed, ambiguous, cancelledinvoiceReferencepaymentHashfailureCodecreatedAtupdatedAt
idpayoutIdinvoiceReferencepaymentHashsenderNodeReferencereceiverReferenceamountBaseUnitsassetIdstatusrawResponseRedactedconfirmedAt
idkind: campaign_rules, daily_events, payout_summarycampaignIdperiodStartperiodEndrootHashrecordCountckbTxHashstatus: pending, submitted, confirmed, failedcreatedAt
idactorUserIdentityTypeentityIdactionbeforeHashafterHashmetadataRedactedcreatedAt
- Store RUSD amounts as decimal strings representing base units.
- Never use JavaScript floating-point arithmetic for rewards or budgets.
- Validate that the reward is positive and within campaign limits.
- Calculate the event reward from canonical campaign rules, not from a client-submitted amount.
- Reserve campaign budget before sending a payment.
- Do not release a reserved amount until the payout is terminal.
- Record the exact asset ID on every payout and payment.
- Reject payments whose invoice amount, asset, receiver, or campaign ID differs from the reservation.
- Record fees separately from participant reward amounts.
- Report paid, failed, ambiguous, and remaining budget totals independently.
draft -> review
review -> scheduled
review -> draft
scheduled -> active
scheduled -> cancelled
active -> paused
active -> closed
paused -> active
paused -> closed
closed -> exported
exported -> archived
- A campaign cannot activate without a valid site, material, reward rule, budget, and operator assignment.
- An active campaign has an immutable rules hash.
- A paused campaign accepts no new accepted events.
- A closed campaign accepts no new event records except administrative reconciliation notes.
- A campaign cannot exceed its participant limit, event limit, or budget cap.
- A campaign cannot be archived until its final export and reconciliation report exist.
received -> validating
validating -> accepted
validating -> rejected
validating -> needs_review
needs_review -> accepted
needs_review -> rejected
accepted -> payout_reserved
payout_reserved -> paid
payout_reserved -> payout_failed
accepted -> cancelled
- Only an assigned operator can create or approve an event.
- The event material must match the campaign material.
- The event site must match the campaign site.
- The event timestamp must be inside the campaign window.
- One event key can create only one event.
- One accepted event can create only one payout.
- A rejected event cannot create a payout.
- A payout failure does not silently change the event back to accepted without an audit record.
- Campaign exists and is active.
- Campaign has not exceeded its end time.
- Operator assignment is valid for the campaign and site.
- Participant is invited, active, and eligible.
- Participant consent version matches the campaign requirement.
- QR payload is authentic, unexpired, and belongs to the participant.
- Material code matches the campaign.
- Quantity is a valid positive integer or bounded decimal string.
- Quantity is within the configured per-event bounds.
- Event timestamp is within the allowed clock skew.
- Event key has not already been used.
- Participant event count has not reached the participant limit.
- Campaign event count has not reached the event limit.
- Remaining budget covers the calculated reward.
- Evidence photo, when supplied, meets type, size, and consent rules.
accepted: all required checks passed.rejected: a deterministic rule failed and no manual review can change the result.needs_review: an operator or reviewer must inspect the event.invalid_request: the API request cannot be parsed or authenticated.
The validation engine returns stable rule codes without exposing private participant data.
| Phase | Scope | Exit evidence |
|---|---|---|
| 0 | Pilot decisions and feasibility | Locked rules and written limitations |
| 1 | Repository and tooling foundation | Workspace builds and CI runs |
| 2 | Configuration and secrets boundary | Validated environment contract |
| 3 | Shared contracts and money types | Contract tests pass |
| 4 | Database schema and migrations | Fresh database migration succeeds |
| 5 | Authentication and roles | Role access tests pass |
| 6 | Sponsor organization and site setup | Site and operator assignment work |
| 7 | Campaign creation | Draft campaign validates |
| 8 | Campaign canonicalization | Stable rules hash is produced |
| 9 | Campaign review and activation | Immutable active campaign exists |
| 10 | Participant invitations | Invite claim works once |
| 11 | QR identity | Authentic QR payloads can be issued and verified |
| 12 | Participant consent | Consent is versioned and auditable |
| 13 | Operator collection screen | Operator can record a draft event |
| 14 | Event intake API | Valid event request is persisted |
| 15 | Deterministic validation | Positive and negative rules pass |
| 16 | Manual review | Review decisions are auditable |
| 17 | Exactly-once event acceptance | Duplicate event race is safe |
| 18 | Payout reservation | One accepted event reserves one payout |
| 19 | Fiber adapter | Invoice and payment contracts are tested |
| 20 | Fiber testnet payment | Confirmed RUSD payment exists |
| 21 | Payment reconciliation | Ambiguous payments are recoverable |
| 22 | Participant payout history | Status and proof are visible |
| 23 | Sponsor recovery dashboard | Totals agree with accepted events |
| 24 | Privacy and evidence storage | Photos are private and governed |
| 25 | Export package | CSV, audit, and reconciliation exports verify |
| 26 | CKB campaign commitment | Campaign rules commitment is confirmed |
| 27 | Daily collection commitment | Daily root and proof verify |
| 28 | Payout summary commitment | Payment summary commitment verifies |
| 29 | Admin safety controls | Holds, limits, and emergency stop work |
| 30 | Notifications and operations | User-facing failure handling works |
| 31 | Unit and integration tests | Core test suite passes |
| 32 | Concurrency and security tests | Race and abuse tests pass |
| 33 | Browser and accessibility tests | Critical workflows pass in browser |
| 34 | Local deployment | Docker Compose starts from empty state |
| 35 | Testnet deployment | Public beta environment is reproducible |
| 36 | Pilot preparation | Partner and participant runbooks are ready |
| 37 | Pilot execution | 200 accepted events and payment evidence exist |
| 38 | Reconciliation and reporting | Physical and digital totals reconcile |
| 39 | Release and handover | Public repository and maintenance package are complete |
Tasks:
- Select one recycler, sponsor, university, community center, or event partner.
- Confirm the exact collection site and operating hours.
- Identify the person responsible for physical acceptance.
- Confirm how material will be stored and counted after collection.
- Record the material type and why it is measurable at the site.
Audit:
- Partner, site, operator, material, and pilot dates are written in
docs/product-decisions.md. - The site can support at least 200 events without changing the rules.
Tasks:
- Choose count or weight as the primary measurement.
- Define the reward per item or unit weight.
- Define minimum and maximum event quantity.
- Define participant and campaign limits.
- Define the total campaign budget.
- Decide how rounding works for weight-based rewards.
Audit:
- A sample rule can be calculated without floating-point arithmetic.
- The participant-facing rule and sponsor-facing rule are identical.
Tasks:
- Select and pin the Fiber testnet node version.
- Confirm the RUSD asset ID and decimal convention.
- Identify the payer node and receiver setup.
- Send a manual testnet payment.
- Record invoice, payment, confirmation, and failure behavior.
Audit:
docs/fiber-compatibility.mdcontains the tested RPC methods and versions.- A manual payment hash is recorded without storing secrets in the repository.
Tasks:
- Select the CKB testnet network and RPC endpoint.
- Confirm the CCC version and known scripts needed for the commitment.
- Choose the commitment cell format.
- Send a minimal test commitment.
- Verify the commitment from a second read path.
Audit:
- The commitment payload contains no participant names, QR secrets, photos, or payment credentials.
- The test transaction and explorer link are recorded in private pilot notes.
Tasks:
- Write the non-goals into the product decisions document.
- Define what the operator is trusted to do.
- Define what the software can detect and what it cannot prove.
- Define testnet-only language for every payment screen.
Exit Criteria:
- Partner, site, material, reward, budget, testnet services, and trust boundaries are approved.
Tasks:
- Create the repository and package manager workspace.
- Add the web, API, worker, and shared package directories.
- Add formatting, linting, type checking, and test scripts.
- Add a root README with local setup instructions.
Tasks:
- Create the participant, operator, sponsor, and admin route groups.
- Add a shared layout and error boundary.
- Add loading, empty, unauthorized, and failure states.
- Add the initial design tokens and responsive shell.
Tasks:
- Create the API process and health route.
- Create the worker process and health check.
- Add typed service boundaries between routes, domain logic, and adapters.
- Add structured redacted logging.
Tasks:
- Run type checking, linting, unit tests, and build in CI.
- Add a database service for integration tests.
- Add dependency lockfile checks.
- Add a diff check that rejects committed secrets.
Exit Criteria:
- A clean checkout installs, checks, tests, and builds without manual edits.
Add typed configuration for:
- database URL;
- Redis URL;
- session secret;
- CKB network and RPC URL;
- CCC configuration;
- Fiber RPC URL and node version;
- RUSD asset ID;
- payer node credentials;
- private evidence storage;
- application origin;
- retention durations.
Tasks:
- Reject missing required values.
- Reject malformed URLs and asset IDs.
- Reject production mode with testnet-disabled safeguards missing.
- Print only safe configuration names and versions.
Tasks:
- Add
.env.examplewith placeholders only. - Document local secret injection.
- Document deployment secret configuration.
- Add a secret scanning check.
Exit Criteria:
- No private key, payer password, QR secret, or storage credential appears in source, tests, logs, or example files.
Implement shared types for roles, campaign states, event states, payout states, commitment states, units, and validation outcomes.
Tasks:
- Parse base-unit strings.
- Add and subtract without floating point.
- Reject negative and malformed values.
- Compare values safely.
- Format values for the UI without changing stored precision.
Define schemas for campaign rules, participant claims, QR payloads, collection events, review decisions, payout requests, exports, and commitment records.
Create valid and invalid fixtures for every shared contract. Keep fixtures deterministic so they can be used by API, worker, and browser tests.
Exit Criteria:
- Every API boundary rejects malformed input before domain services run.
- Money and status types are imported from one shared package.
Implement User, ParticipantProfile, SponsorOrganization, CollectionSite, and OperatorAssignment.
Implement Campaign, campaign rule versions, activation records, and budget reservations.
Implement CollectionEvent, ValidationFinding, review decisions, and event evidence references.
Implement Payout, FiberPayment, invoice attempts, reconciliation attempts, and idempotency keys.
Implement AuditEvent, Commitment, commitment records, and export manifests.
Add database enforcement for:
- unique event keys;
- unique payout per event;
- unique payout idempotency key;
- unique operator assignment where required;
- valid state values;
- nonnegative amounts;
- campaign and site lookup indexes;
- payout and commitment reconciliation indexes.
Tasks:
- Run migrations against an empty database.
- Seed a minimal pilot.
- Roll back only in disposable test environments.
- Test transaction boundaries for event and payout creation.
Exit Criteria:
- A fresh database can run the application and seed fixtures with no manual SQL.
Tasks:
- Choose the beta authentication method.
- Create secure sessions with expiration and rotation.
- Add logout and session invalidation.
- Avoid placing private participant or payer data in browser-readable tokens.
Implement server-side guards for participant, operator, sponsor, reviewer, and admin actions.
Ensure a sponsor can access only its organization, an operator only assigned sites, and a participant only its own records.
Add tests for anonymous access, wrong role, wrong organization, wrong campaign, and suspended users.
Exit Criteria:
- Every protected route has a server-side authorization test.
Tasks:
- Create organization record.
- Add sponsor administrators.
- Store partner reference without exposing private agreements.
- Add organization status controls.
Tasks:
- Store site label, timezone, operating hours, and instructions.
- Avoid collecting participant residence or unrelated location data.
- Add active and inactive states.
Tasks:
- Assign an operator to a site and campaign.
- Add validity dates.
- Revoke assignment without deleting historical event records.
Create sponsor and admin screens for site details and operator assignments with clear active-state indicators.
Exit Criteria:
- A campaign cannot use a site without an active operator assignment.
Collect:
- campaign name;
- material;
- site;
- count or weight mode;
- reward rule;
- event bounds;
- participant and event limits;
- budget;
- start and end time;
- consent and retention version.
Reject missing material, invalid time windows, zero rewards, invalid bounds, insufficient budget, and unassigned sites.
Display an example event and calculated reward before the campaign is submitted for review.
Implement create, update, read, and list operations with organization scope and audit events.
Exit Criteria:
- A sponsor can create a valid draft and receives stable validation errors for invalid rules.
Sort object keys, normalize timestamps, normalize material codes, normalize decimal strings, and omit non-rule metadata.
Create a deterministic hash from the canonical rule document.
Show changed rules between versions before activation. Never silently mutate an activated version.
Test key ordering, whitespace, timestamp representation, decimal representation, and omitted metadata.
Exit Criteria:
- Identical rules always produce the same hash across processes.
- A meaningful rule change always produces a different hash.
Show draft rules, sponsor, site, operator, budget, consent version, and rules hash.
Allow an authorized reviewer to approve or return a campaign with a reason. Record before and after hashes.
Require a valid campaign, active site, operator assignment, reward configuration, budget, participant limit, and rules hash.
Prevent edits to any value that affects eligibility, reward, limits, or commitment content. Changes require a new campaign or version.
Exit Criteria:
- An active campaign has a stable rules hash and cannot be changed through API or UI shortcuts.
Create single-use or bounded invite codes linked to a campaign and expiry time.
Tasks:
- Validate invite status and expiry.
- Create or link participant account.
- Prevent a code from being claimed twice.
- Record claim audit event.
Display site, material, reward, limits, campaign dates, purpose, privacy policy, and testnet payment disclosure.
Prevent suspended participants from creating new events while preserving their history and payout records.
Exit Criteria:
- A participant can claim one campaign invite and see only the campaign information relevant to them.
The payload should contain a short participant reference, campaign scope where appropriate, expiry, nonce, and an integrity mechanism. It must not contain a private secret or unnecessary personal data.
Create a participant QR screen with regeneration and revocation controls.
Verify authenticity, expiry, campaign scope, participant status, and replay protection on the server.
Add camera scanning and manual short-code fallback for supported devices and controlled pilot recovery.
Test expired, malformed, copied, wrong-campaign, revoked, replayed, and suspended QR payloads.
Exit Criteria:
- An operator can identify an eligible participant without seeing unnecessary personal information.
Create a versioned consent document covering purpose, requested event data, photos, retention, sponsor access, and payment disclosure.
Require participant acceptance before event collection. Store version, timestamp, and policy hash.
Prevent new submissions after withdrawal and document how retained financial and audit records are handled.
Delete or anonymize expired photos and private raw evidence without deleting the minimum records needed for payment and audit obligations.
Exit Criteria:
- Consent is required by the API, not only by the participant UI.
Show assigned campaigns, site, operating window, material, active limits, and recent events.
Require QR identity, selected campaign, material confirmation, and measurement mode.
Validate the displayed unit, precision, minimum, and maximum before submission. Provide a clear confirmation step.
Allow an optional photo only after showing consent and storage behavior. Store the photo privately.
Show participant pseudonym, material, quantity, reward estimate, site, operator, and timestamp before final approval.
Exit Criteria:
- Operator can record a complete event without editing a campaign rule or participant payout amount.
Require campaign ID, participant reference, QR proof, material code, quantity, unit, client event key, timestamp, and evidence reference where applicable.
Check role, assignment, site, and campaign state before accepting the request.
Create the event in received state inside a database transaction. Do not mark it accepted in the route handler.
Publish one validation job keyed by event ID and return a status response that the UI can poll.
Repeated requests with the same event key return the existing event and never create a second event.
Exit Criteria:
- Intake is authenticated, idempotent, and does not bypass the validation engine.
Reject inactive, expired, paused, cancelled, or closed campaigns.
Reject operators without current assignment to the campaign site.
Reject unknown, suspended, withdrawn, or over-limit participants.
Reject wrong material, invalid units, zero values, negative values, excessive precision, and out-of-range quantities.
Reject events outside campaign dates, outside site hours, or outside the accepted clock-skew window.
Check event key, recent participant events, campaign event cap, participant cap, and remaining budget.
Calculate the reward only from immutable campaign rules and canonical quantity. Store the calculation inputs and rule version.
Record stable rule codes and redacted details. Do not store raw secrets in findings or logs.
Exit Criteria:
- The same input and campaign version always produce the same decision and reward.
Route events to review for configured anomaly conditions such as unusual quantity, evidence mismatch, or operational exception.
Show only information needed to decide the event. Redact private data by default.
Require a reviewer reason code and record the reviewer, timestamp, prior status, and new status.
Do not let a reviewer change campaign reward, participant identity, event quantity, or payment amount during review.
Exit Criteria:
- Every manual decision is auditable and cannot create a payout without a valid accepted event.
Enforce unique event keys and prevent concurrent acceptance of the same event.
Within one transaction, verify remaining budget and reserve the calculated reward for the accepted event.
Retries return the original decision and reservation instead of creating a new record.
Run parallel requests for the same participant, event key, campaign limit, and final budget unit.
Exit Criteria:
- No race can create two accepted records or reserve more than the campaign budget.
Create one payout for one accepted event with amount, asset, participant receiver, campaign, and idempotency key.
Require a participant receiver that is present, approved for the pilot, and not changed after reservation without a controlled reauthorization.
Enqueue the payout using a stable job key. Do not call Fiber from the event request.
Allow only valid transitions between reserved, invoice created, submitted, confirming, paid, failed, ambiguous, and cancelled.
Exit Criteria:
- Every accepted event has at most one payout row and every payout row points to one event.
Document the tested node version and methods for invoice creation, payment submission, payment lookup, and invoice lookup.
Add timeouts, request IDs, redacted errors, response schema validation, and bounded retries for safe read operations.
Validate receiver, amount, asset, expiry, description, and campaign reference before payment.
Allow only the settlement worker to submit a payment. Attach payout idempotency metadata where supported.
Normalize payment states from the pinned node version into the internal state model.
Exit Criteria:
- Fake-node tests cover success, rejection, timeout, malformed response, duplicate request, and unknown status.
Document funding, node health, asset balance, receiver setup, and maximum pilot exposure.
Generate a receiver invoice from the approved participant receiver reference and compare it to the reserved payout.
Submit the payment once and record the payment hash or terminal rejection.
Poll according to the pinned node behavior. Do not mark a payout paid from a submission response alone when confirmation is required.
Show pending, paid, failed, and needs-reconciliation states without exposing payer credentials or internal RPC responses.
Exit Criteria:
- At least one confirmed RUSD testnet payment is linked to an accepted event and visible in the audit report.
Separate validation failure, invoice failure, transport failure, rejected payment, timeout, and ambiguous payment.
Search by idempotency reference, invoice, and payment hash before retrying any uncertain request.
Create an operator-visible reconciliation record when a payment cannot be matched safely.
Do not submit another payment while a prior payment may have succeeded.
Provide a restricted command for checking and resolving an ambiguous payout with an audit reason.
Exit Criteria:
- A worker restart, timeout, or duplicate job cannot pay the same event twice.
Show participant events, accepted quantity, reward, event status, and decision reason where safe.
Show payout pending, paid, failed, or reconciliation-required status with plain-language explanations.
Display payment hash, asset, amount, and testnet explorer link only after the data is confirmed.
Explain that a failed payout is not silently discarded and show the next operational action.
Exit Criteria:
- A participant can understand whether an event was accepted and whether its reward was paid.
Show total accepted quantity, event count, participant count, rejected count, review count, paid amount, pending amount, and remaining budget.
Display totals in the selected material unit and preserve the original count or weight semantics.
Separate reserved, paid, failed, ambiguous, and cancelled amounts.
Show event volume and rejection rates by site and operator without exposing unnecessary participant data.
Calculate dashboard totals from accepted event and payout records, not from mutable client filters.
Exit Criteria:
- Dashboard totals can be independently reproduced from the audit export.
Use private objects, short-lived access URLs, size limits, content-type checks, and server-side ownership checks.
Reject executable content, unsupported types, oversized files, and uploads without a matching event request.
Strip unnecessary EXIF and location metadata before review storage where practical.
Allow access only to authorized operators, reviewers, and sponsor roles with campaign scope.
Delete or anonymize evidence after the documented retention period and record the deletion audit event.
Exit Criteria:
- No evidence photo is public by default or included in CKB commitment content.
Create a stable export containing event ID, campaign reference, material, quantity, unit, decision, date bucket, and redacted participant code.
Include payout ID, event ID, amount, asset, status, invoice reference, payment hash, and confirmation time.
Combine physical partner totals, accepted event totals, rejected totals, and Fiber payout totals with explicit variance fields.
Record file hashes, row counts, campaign rules hash, generation time, and privacy policy version.
Generate the same export twice from unchanged data and confirm identical canonical content and hash.
Exit Criteria:
- Exports contain enough information for audit without publishing participant contact data or photos.
Commit only campaign ID hash, rules hash, material code, site reference hash, campaign period, and public version metadata.
Use a deterministic byte representation and document field order and encoding.
Create the testnet commitment transaction with the selected cell format and signer boundary.
Record transaction hash, block reference, payload hash, and explorer link after confirmation.
Read the commitment from CKB and compare it to the campaign rules hash stored by the application.
Exit Criteria:
- An active campaign has a confirmed CKB commitment before participant events are accepted.
Select accepted events and their terminal payout states for a closed UTC or campaign-local day.
Hash only privacy-filtered fields with stable ordering and explicit version information.
Create a deterministic root and store leaf count, root hash, and export manifest hash.
Submit the root through CCC and wait for testnet confirmation.
Provide a command and UI view that verifies an event leaf against the committed root.
Exit Criteria:
- A reviewer can verify that a selected accepted event belongs to the committed daily record set.
Canonicalize accepted count, total quantity, paid amount, pending amount, failed amount, asset ID, and payment hashes.
Create a separate summary hash so recovery and payment totals can be verified without exposing raw participant data.
Submit the payout summary commitment to CKB and store the confirmed transaction.
Compare application totals, export totals, partner reconciliation totals, and the committed summary.
Exit Criteria:
- The final report links campaign, daily records, payout summary, exports, and CKB transactions.
Allow an authorized admin to pause new event acceptance without deleting existing records.
Block new payout submission while allowing reconciliation and read-only operations.
Enforce maximum campaign, daily, participant, and worker payout exposure.
Revoke a compromised or unavailable operator assignment immediately.
Record actor, scope, reason, previous state, new state, and timestamp for each control change.
Exit Criteria:
- A safety control blocks the relevant operation in both API and worker paths.
Notify participants about event acceptance, rejection, payout pending, payout success, and payout delay.
Alert operators to invalid QR codes, campaign closure, duplicate attempts, and offline queue failures.
Alert sponsors to budget thresholds, payout failures, commitment failures, and reconciliation variance.
Do not include payment credentials, private URLs, QR secrets, or raw evidence details in notifications.
Exit Criteria:
- Every terminal user-visible state has a clear explanation and an operational next step.
Test valid and invalid schemas, status transitions, money arithmetic, canonicalization, and hash stability.
Test campaign activation, participant eligibility, event validation, reward calculation, review, budget reservation, and state transitions.
Test migrations, constraints, organization scope, event uniqueness, payout uniqueness, and transaction rollback.
Test authentication, authorization, validation errors, idempotency, pagination, and redacted responses.
Test duplicate jobs, worker restart, payout retry, commitment retry, export retry, and dead-letter handling.
Exit Criteria:
- All core domain behavior has deterministic tests independent of external nodes.
Send concurrent requests with the same event key and confirm one event and one payout reservation.
Submit events that together exceed the final budget and confirm only the allowed amount is accepted.
Test invite reuse, QR replay, rapid submissions, participant cap bypass, and suspended accounts.
Test wrong-site submissions, revoked assignments, altered quantities, and access to other campaigns.
Test receiver substitution, asset substitution, amount mutation, duplicate payment jobs, and ambiguous status.
Document threats, controls, residual risks, trusted operators, storage risks, payer risks, and CKB limitations.
Exit Criteria:
- All high-risk findings have a mitigation, documented acceptance, or a release blocker.
Test invite claim, consent, QR display, event history, and payout status.
Test campaign selection, QR scan, event entry, photo consent, review, and retry behavior.
Test campaign creation, review, dashboard, exports, commitments, and reconciliation.
Test offline submission, expired campaign, duplicate QR, payment pending, Fiber failure, CKB failure, and unauthorized access.
Check keyboard navigation, labels, focus states, contrast, error announcements, touch target size, and mobile layout.
Exit Criteria:
- Critical flows pass in supported desktop and mobile browsers without exposing raw internal JSON as the primary user experience.
Run web, API, worker, PostgreSQL, Redis, and local object storage with documented health checks.
Run migrations and seed a complete pilot fixture from one command.
Provide a deterministic fake adapter for local development. Label all mock payments clearly in the UI and reports.
Provide a deterministic commitment adapter for tests without confusing it with testnet confirmation.
Document startup, reset, seed, test, worker execution, export generation, and troubleshooting commands.
Exit Criteria:
- A new developer can run the entire happy path and negative cases from a clean checkout.
Provision the database, Redis, private object storage, web app, API, worker, and monitoring.
Pin node endpoint, payer credentials, receiver configuration, RUSD asset ID, timeout, and confirmation policy.
Configure RPC, network, CCC settings, signer boundary, and explorer links.
Run health checks, test invoice creation, test a small payment, test a commitment, and verify logs are redacted.
Document environment variables, migrations, worker startup, backups, rollback, and emergency stop procedures.
Exit Criteria:
- Testnet deployment is reproducible and has no test credentials in the repository.
Document collection procedure, accepted material, quantity measurement, event correction, storage, and daily closeout.
Train operators on QR verification, event preview, rejection reasons, photo consent, offline recovery, and escalation.
Invite up to 50 participants, collect consent, verify contact path, and provide reward instructions.
Print or distribute site and participant QR materials with revocation and replacement procedures.
Fund the testnet payer, set the campaign budget, verify receiver setup, and enable exposure limits.
Complete a full mock event from QR scan through export and commitment without using real pilot records.
Exit Criteria:
- Partner, operators, participants, funding, materials, and escalation contacts are ready.
Activate the campaign only after campaign rules commitment and operator readiness checks pass.
Track events, rejections, reviews, accepted quantities, budget, and operator activity.
Run payout workers with confirmation and reconciliation controls. Record every payment hash.
Generate the daily export, reconcile physical totals, build the Merkle root, and commit the daily record hash.
Use pause, operator revocation, payout emergency stop, and reconciliation procedures when required.
Complete at least 200 accepted collection events while tracking rejected, reviewed, failed, and ambiguous cases separately.
Exit Criteria:
- Pilot records, payouts, commitments, operator logs, and incident records are complete.
Compare partner weigh-in or count records with accepted digital event totals by day and material.
Compare database events, exports, payout reservations, Fiber confirmations, and CKB commitment metadata.
Document rejected events, review decisions, measurement differences, failed payments, and timing differences.
Include recovered material, accepted events, rejected events, participant count, payout totals, budget remaining, payment hashes, commitment links, and limitations.
Publish only privacy-filtered totals and evidence. Do not publish participant identities, raw photos, payer details, or private infrastructure data.
Exit Criteria:
- A reviewer can trace the sponsor summary to exports, payment records, and CKB commitments without accessing private participant data.
Tag the tested application, worker, contract package, database schema, Fiber adapter version, and commitment format.
Publish setup, architecture, privacy, validation, deployment, partner, pilot, and troubleshooting documentation.
Include test results, testnet payment hashes, CKB transaction hashes, export manifests, reconciliation report, and known limitations.
Document payer funding, worker monitoring, backups, retention jobs, emergency stop, operator support, and incident response.
Plan three months of maintenance for dependency updates, Fiber compatibility, bug fixes, partner support, and pilot report corrections.
Exit Criteria:
- The public repository, deployment, evidence, operational runbook, and maintenance ownership are complete.
POST /api/auth/invite/claimPOST /api/auth/logoutGET /api/mePATCH /api/me/consent
POST /api/organizationsGET /api/organizations/:idPOST /api/sitesPATCH /api/sites/:idPOST /api/sites/:id/operatorsDELETE /api/sites/:id/operators/:userId
POST /api/campaignsGET /api/campaignsGET /api/campaigns/:idPATCH /api/campaigns/:idPOST /api/campaigns/:id/submit-reviewPOST /api/campaigns/:id/approvePOST /api/campaigns/:id/activatePOST /api/campaigns/:id/pausePOST /api/campaigns/:id/close
POST /api/campaigns/:id/invitesPOST /api/participants/qr/rotateGET /api/participants/eventsGET /api/participants/payouts
POST /api/eventsGET /api/events/:idPOST /api/events/:id/validatePOST /api/events/:id/review/acceptPOST /api/events/:id/review/rejectPOST /api/events/:id/evidence
GET /api/payoutsGET /api/payouts/:idPOST /api/payouts/:id/reconcilePOST /api/admin/payouts/emergency-stop
POST /api/campaigns/:id/exportsGET /api/exports/:idPOST /api/campaigns/:id/commitments/campaignPOST /api/campaigns/:id/commitments/dailyPOST /api/campaigns/:id/commitments/payoutsGET /api/commitments/:idGET /api/commitments/:id/proof/:recordId
- invite claim and sign in;
- campaign briefing;
- consent;
- QR identity;
- event history;
- payout history;
- payment detail;
- privacy and retention settings;
- assigned campaign list;
- QR scanner;
- event entry;
- evidence capture;
- event confirmation;
- recent event queue;
- rejected and review-needed events;
- site closeout summary;
- organization overview;
- site and operator setup;
- campaign builder;
- campaign review;
- active campaign dashboard;
- recovery metrics;
- payout metrics;
- export center;
- CKB commitment evidence;
- reconciliation report;
- users and roles;
- campaign safety controls;
- payout emergency stop;
- reconciliation queue;
- audit events;
- system health;
- retention and storage status;
- Lock pilot partner, site, material, rules, and limitations.
- Create workspace, applications, packages, CI, and configuration.
- Implement contracts, money utilities, and validation schemas.
- Implement database migrations and authorization boundaries.
- Build organization, site, operator, campaign, and activation workflows.
- Implement participant invites, consent, and QR identity.
- Build operator collection and event intake.
- Implement deterministic validation and manual review.
- Add exactly-once acceptance and budget reservation.
- Implement payout records and the Fiber adapter.
- Run mock settlement tests and then confirm a funded testnet payment.
- Build participant payout history and sponsor dashboards.
- Add evidence storage, retention, exports, and reconciliation.
- Implement campaign, daily event, and payout summary commitments.
- Add admin safety controls, notifications, and operations tooling.
- Complete security, concurrency, browser, accessibility, and deployment tests.
- Run the pilot, reconcile physical and digital totals, and publish the evidence bundle.
Includes Phases 0-9:
- partner and site selection;
- campaign rules;
- repository and infrastructure foundation;
- shared contracts;
- database;
- organization, operator, and campaign setup;
- campaign canonicalization and activation.
Includes Phases 10-17:
- invitations;
- QR identity;
- consent;
- operator collection;
- event intake;
- validation;
- manual review;
- exactly-once acceptance.
Includes Phases 18-28:
- payout reservations;
- Fiber adapter;
- confirmed RUSD payments;
- reconciliation;
- participant and sponsor reporting;
- private evidence storage;
- exports;
- campaign, daily, and payout commitments.
Includes Phases 29-39:
- safety controls;
- notifications;
- automated and security testing;
- browser and accessibility testing;
- local and testnet deployment;
- partner preparation;
- 200-event pilot;
- reconciliation;
- public release and maintenance handover.
- Do not describe an operator-approved collection record as trustless physical proof.
- Do not accept an event without a server-side operator assignment check.
- Do not calculate rewards from client-submitted amounts.
- Do not use floating-point arithmetic for RUSD or campaign budgets.
- Do not let a retry create a second event or payout.
- Do not call Fiber spending RPCs from browser code.
- Do not retry ambiguous payments until they are reconciled.
- Do not expose payer credentials, QR secrets, private object URLs, or raw private evidence.
- Do not mutate an activated campaign rule set.
- Do not put personal information, photos, or payment secrets in CKB commitment payloads.
- Do not present mock Fiber or mock CKB results as live evidence.
- Do not call a payout paid until the required confirmation check succeeds.
- Do not publish a reconciliation total that cannot be reproduced from the export manifest.
These decisions must be closed during the first validation work and recorded before implementation depends on them:
- exact Fiber node and RPC version;
- exact RUSD testnet asset ID and base-unit precision;
- participant authentication method;
- CKB commitment cell format;
- CCC signer and funding boundary;
- object storage provider and retention configuration;
- QR payload signing or integrity method;
- count versus weight measurement for the selected material;
- accepted clock-skew window;
- event correction policy after operator approval;
- daily commitment timezone;
- sponsor report publication policy;
- minimum browser and mobile support;
- support process for lost participant QR identity;
- pilot photo retention duration.
- A sponsor can create and activate one bounded campaign.
- An operator can verify a participant QR identity at the correct site.
- An operator can record count or weight and optional evidence.
- A participant can see event and payout status.
- A sponsor can inspect recovery and payment totals.
- Duplicate events are rejected.
- Wrong campaign, material, site, operator, participant, time, and quantity are rejected.
- Participant, event, and budget limits are enforced under concurrency.
- Review decisions are permissioned and audited.
- At least one confirmed RUSD testnet payment exists.
- Payout reservation is exactly once.
- Payment amounts and assets are verified against invoices.
- Ambiguous payments are reconciled without duplicate settlement.
- Campaign rules commitment is confirmed.
- Daily collection-record commitment is confirmed.
- Payout summary commitment is confirmed.
- Inclusion and export verification tools work.
- Payer credentials never reach clients.
- QR secrets and private evidence are protected.
- Public exports do not expose participant contact data or photos.
- Retention and deletion behavior is documented and tested.
- High-risk threat-model findings are resolved or explicitly accepted.
- Repository is public and reproducible.
- Test results and build instructions are published.
- Fiber transaction hashes and CKB transaction hashes are recorded.
- At least 200 accepted events are included in the pilot evidence.
- Physical-versus-digital reconciliation is documented.
- Demonstration video shows the participant, operator, sponsor, payout, and audit workflows.
- Three-month maintenance responsibilities are assigned.