CrowdSource is participatory moderation infrastructure.
Reports become cases, a jury drawn at random reviews them blind, and a versioned decision goes back by webhook.
|
Applications collect reports and then resolve them with internal panels, individual judgement, or processes that do not scale. The person who was reported usually gets no explanation and no appeal. CrowdSource replaces that with one auditable process. Duplicate reports merge into a single case. Evidence is preserved as it was when reported. Independent reviewers evaluate it blind. A consensus engine decides. An appeal creates a new jury rather than editing the original decision. The infrastructure is centralised. The decisions are not. |
Not a public feed of accusations, and not open voting. Access to a case is granted by the server, temporary and blind. Not a way for an application to subtract points from someone. Only a finished moderation decision can produce a reputation effect, and only Oxy Trust decides what that effect is. Not a permanent archive of everything anyone reported, and not a universal blocklist without context, appeal or expiry. Not an AI judge. Automation may prioritise, translate and detect duplicates. It does not decide. |
These come from the approved product specification. They are not preferences. A change that violates one is wrong even if it passes review.
- Nobody chooses the case they review. The server issues an assignment. There is no case search, no shareable case link, no public queue.
- A reviewer never sees partial votes, other jurors' identities, the reporter's identity, or anyone's reputation.
- One qualified person, one vote. Reputation affects eligibility and selection probability, never the weight of a vote inside a jury.
- A published decision is never edited, only superseded. An appeal creates a new revision.
- One penalty per incident. A hundred reports about the same material produce one case and one consequence.
- Every effect is idempotent, explainable and reversible, and carries the policy version it was decided under.
- Sensitive content never reaches logs, metrics or attestations.
- Absence of consensus is neither guilt nor innocence.
inconclusiveis its own outcome and never collapses intono_violation. applicationIdcomes from the credential, never from the request body.- No binding proof, no reputation effect. An application emits a report; CrowdSource emits a decision.
| Package | Path | What it holds |
|---|---|---|
@crowdsource.you/contracts |
packages/contracts |
The versioned contracts every surface agrees on, as Zod schemas and JSON Schema: case envelope, resources, taxonomy, policies, reviews, decisions, webhooks, reputation events |
@crowdsource/backend |
packages/backend |
PostgreSQL/Drizzle Express 5 modular monolith: RLS-scoped tenancy, ingestion, evidence, cases, sortition, review, consensus, decisions and webhook delivery |
@crowdsource/reviewer |
packages/reviewer |
The reviewer app, Expo Router and React Native Web from one codebase |
@crowdsource/console |
packages/console |
Developer and Trust and Safety console, Expo Router on the web only |
@crowdsource.you/core |
packages/core |
TypeScript client for integrators: reports, cases, decisions. Three further entry points ship inside it — see below |
Each package README says what that package holds.
| Import | Source | What it holds |
|---|---|---|
@crowdsource.you/core |
src |
The API client: reports, cases, decisions, webhook endpoints |
@crowdsource.you/core/express |
src/express |
Express webhook receiver: raw body capture, HMAC verification, replay protection, typed events |
@crowdsource.you/core/outbox |
src/outbox |
PostgreSQL-only application integration: transactional outbox, delivery, receiver, decision application and idempotent enforcement |
@crowdsource.you/core/testing |
src/testing |
Fixtures, a webhook simulator and an in process sandbox, so you can integrate before a jury has ever sat |
Two published packages, not five. contracts stays separate because it must
exist exactly once in a consumer's tree — two copies type check clean and fail
every delivery at runtime — and because a React Native UI needs its types without
a server only client in the phone bundle. The rest is one package, so a client and
a receiver installed together cannot be different versions of the same agreement.
Importing the root pulls in no express, drizzle-orm, postgres or
@oxy.so/db: they are optional peers reached only through the subpaths.
Coming from the old names: @oxy.so/crowdsource-contracts is now
@crowdsource.you/contracts, @oxy.so/crowdsource is @crowdsource.you/core,
and -express, -app and -testing are its /express, /outbox and
/testing entry points. The full table is in
packages/core/README.md.
An adopting application writes four things and nothing else: its subject providers, its category to allegation mapping, its enforcement tables plus one apply, and its own PostgreSQL report table. Everything it would otherwise copy, the outbox, delivery, the webhook receiver, cross instance dedupe, decision application, the enforcement claim and the enforcement planning algorithm, lives in @crowdsource.you/core/outbox.
bun add @crowdsource.you/core @crowdsource.you/contracts expressTwo rules follow from that design and bind every change to those packages.
- The service key carries the tenant. An integrator configures
applicationId:credentialId:secret, so the client already knows which application it is. There is noapplicationIdoption, field or parameter to pass, and the copy inside the envelope exists only so a mismatch can be detected. - Nothing the client composes may vary between two deliveries of the same report. Ingress fingerprints the whole payload to detect conflicts, so an invented timestamp, a random id or an unsorted list turns a legitimate outbox retry into a permanent conflict, silently, days later.
Start with the integration guide.
Requires Bun 1.4.2 and Node.js 22.17.0 for the Expo and Jest toolchains.
bun install
bun run dev:backend # http://localhost:3000/health/ready
bun run dev:reviewer # reviewer app, Expo
bun run dev:console # console, Expo webCopy packages/backend/.env.example and packages/reviewer/.env.example to .env for local configuration.
Checks, builds and tests
bun run check # the same gate CI runs
bun run build # contracts, backend, SDKs
bun run build:reviewer # reviewer static web export
bun run build:console # console static web export
bun run test # vitest for the backend and the libraries, jest for the two Expo apps
bun run lint # Biome format + lint over the whole tree, per-package tsc, Expo env-var ESLint
bun run format # apply Biome formattingcheck is the whole gate: workspace doctor, workflow validation, security audit, injection sink scan, peer contract check, build, published shape checks, type check and lint. Formatting and linting are Biome (biome.json at the root, covering source, tests and scripts alike); the two Expo apps keep a minimal ESLint config for eslint-plugin-expo's env-var rules only.
Rebuild contracts before you believe a red type check. Every other package imports the built shape, never src, so after a rebase that lands a contracts change the rest of the repo still compiles against the previous build and reports the new symbols as missing in files nobody touched.
CrowdSource is a product of its own, not a feature of any application that uses it. It began as a fork of Mention, taken for the Expo and Bloom monorepo foundation only, and shares no data, identity, service or deployment with it.
Reviewers, developers and Trust and Safety staff sign in with their Oxy account. The apps mount one OxyProvider from @oxy.so/services and the backend verifies sessions with @oxy.so/core/server, in one place, never an app local bearer parser. Applications calling the ingestion API use CrowdSource's own service credentials instead, and the two authentication surfaces never satisfy each other's routes.
Reputation only ever moves in one direction. CrowdSource emits an authenticated event and Oxy Trust's own consequence engine decides the effect.
| Area | Contents |
|---|---|
| Integration | Connecting an application end to end |
| Architecture | The universal case envelope, appeals, reputation axes, the threat model, and where the implementation diverges from the plan |
| API | The application, reviewer, console and webhook surfaces |
| Policies | The policy sets cases are decided under |
| Runbooks | Webhook dead letters, outbox backlog, a case that cannot empanel, audit trails |
Instructions for AI coding agents live in AGENTS.md.
Issues and pull requests are welcome, and criticism of the design is the most useful kind. Please run bun run check and bun run test first. Org wide contributing notes, the security policy and the code of conduct live in the organisation profile.
MIT. See LICENSE.