@oxyhq/crowdsource-contracts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/case-envelope.d.ts +1130 -0
- package/dist/case-envelope.d.ts.map +1 -0
- package/dist/case-envelope.js +383 -0
- package/dist/case-envelope.js.map +1 -0
- package/dist/decisions.d.ts +353 -0
- package/dist/decisions.d.ts.map +1 -0
- package/dist/decisions.js +198 -0
- package/dist/decisions.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/json-schema.d.ts +43 -0
- package/dist/json-schema.d.ts.map +1 -0
- package/dist/json-schema.js +83 -0
- package/dist/json-schema.js.map +1 -0
- package/dist/policies.d.ts +286 -0
- package/dist/policies.d.ts.map +1 -0
- package/dist/policies.js +178 -0
- package/dist/policies.js.map +1 -0
- package/dist/primitives.d.ts +185 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +231 -0
- package/dist/primitives.js.map +1 -0
- package/dist/reputation-events.d.ts +349 -0
- package/dist/reputation-events.d.ts.map +1 -0
- package/dist/reputation-events.js +128 -0
- package/dist/reputation-events.js.map +1 -0
- package/dist/resources.d.ts +484 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +436 -0
- package/dist/resources.js.map +1 -0
- package/dist/reviews.d.ts +276 -0
- package/dist/reviews.d.ts.map +1 -0
- package/dist/reviews.js +144 -0
- package/dist/reviews.js.map +1 -0
- package/dist/taxonomy.d.ts +266 -0
- package/dist/taxonomy.d.ts.map +1 -0
- package/dist/taxonomy.js +282 -0
- package/dist/taxonomy.js.map +1 -0
- package/dist/webhooks.d.ts +604 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +192 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +56 -0
- package/src/case-envelope.ts +433 -0
- package/src/decisions.ts +216 -0
- package/src/index.ts +45 -0
- package/src/json-schema.ts +89 -0
- package/src/policies.ts +203 -0
- package/src/primitives.ts +283 -0
- package/src/reputation-events.ts +144 -0
- package/src/resources.ts +489 -0
- package/src/reviews.ts +159 -0
- package/src/taxonomy.ts +313 -0
- package/src/webhooks.ts +215 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @oxyhq/crowdsource-contracts
|
|
2
|
+
|
|
3
|
+
The versioned contracts every CrowdSource surface agrees on — the backend, the
|
|
4
|
+
reviewer and console clients, the published SDKs, and third-party integrators.
|
|
5
|
+
|
|
6
|
+
Authored with Zod; the same contracts are exported as JSON Schema
|
|
7
|
+
(`crowdSourceJsonSchema(name)`) so integrators who are not on TypeScript
|
|
8
|
+
validate against the same definition.
|
|
9
|
+
|
|
10
|
+
## Rules
|
|
11
|
+
|
|
12
|
+
- Contracts only. No application logic, no transport clients, no runtime that
|
|
13
|
+
belongs to one surface.
|
|
14
|
+
- `schemaVersion` travels inside the payload (`crowdsource.case.v1`) and is
|
|
15
|
+
validated separately from the `/v1` route version. Additive changes bump
|
|
16
|
+
neither.
|
|
17
|
+
- A published contract version is immutable. Widening is additive; narrowing
|
|
18
|
+
needs a new version.
|
|
19
|
+
- Nothing here may depend on a single tenant's vocabulary. Application-specific
|
|
20
|
+
subject types and policies are data, registered per application, never types
|
|
21
|
+
in this package.
|
|
22
|
+
|
|
23
|
+
## Where strictness lands, and why
|
|
24
|
+
|
|
25
|
+
§10.11 says unknown fields must not break clients, "except where the schema
|
|
26
|
+
forbids them for safety". Both halves of that sentence are load-bearing, so the
|
|
27
|
+
package splits by direction rather than picking one setting.
|
|
28
|
+
|
|
29
|
+
| Direction | Setting | Reason |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| Inbound from a tenant or reviewer — the Case Envelope tree, review submissions, recusals, policy sets, resource-schema registrations | `.strict()` | A silently dropped field is context an application believes it sent and a jury never sees, or an unreviewed rendering input. A review submission additionally must not tolerate `caseId` / `assignmentId` / `reviewerId`: those come from the assignment the server issued, and "nobody chooses the case they review" is only true if they cannot be supplied. |
|
|
32
|
+
| Outbound to a tenant — decisions, webhook envelopes, event payloads | `.loose()` | A newer CrowdSource must never break an older client, and a receiver that persists `event.data` for later processing keeps everything that arrived rather than a stripped subset. |
|
|
33
|
+
| Internal, to Oxy Trust — the reputation event | `.strict()` | The event deliberately carries no resource ids and no free text; an unrecognised field is exactly how content reaches a reputation ledger or a signed attestation. Evolution is handled by the `.v1` in the event type. |
|
|
34
|
+
|
|
35
|
+
Open bags — envelope `metadata`, §5.7 custom payloads, registered JSON Schemas —
|
|
36
|
+
are the exception in both directions. They are open by definition but flat or
|
|
37
|
+
depth-bounded, scalar-typed where they can be, key-restricted, and free of
|
|
38
|
+
prototype-bearing names.
|
|
39
|
+
|
|
40
|
+
There is deliberately **no lexical filter on content**. CrowdSource carries the
|
|
41
|
+
reported material, which is hostile by nature: a harassment report quotes the
|
|
42
|
+
harassment, a phishing report quotes the link. A blocklist on text values would
|
|
43
|
+
reject the evidence and protect nothing. §5.7's boundary is structural — the
|
|
44
|
+
contract has no field anywhere whose value is ever interpreted as markup, a
|
|
45
|
+
template, a component or a remote reference.
|
|
46
|
+
|
|
47
|
+
## What the JSON Schema does not carry
|
|
48
|
+
|
|
49
|
+
Zod refinements have no JSON Schema equivalent and are dropped by the
|
|
50
|
+
conversion. Everything structural survives; every cross-field and
|
|
51
|
+
cross-reference rule does not — §5.5 reference resolution, the `oxy_user`
|
|
52
|
+
binding-proof requirement, exactly-one-of `uploadId`/`url`, media type
|
|
53
|
+
agreement, coarse coordinates, the jury arithmetic, the supersession chain.
|
|
54
|
+
|
|
55
|
+
A payload that passes the JSON Schema is well-formed, not accepted. The server
|
|
56
|
+
validates with Zod and that is the authority.
|
|
57
|
+
|
|
58
|
+
## Ambiguities in the plan, and how they were resolved
|
|
59
|
+
|
|
60
|
+
The plan was written as prose, so some of its examples disagree with each other.
|
|
61
|
+
Every such case is listed here; none was resolved silently.
|
|
62
|
+
|
|
63
|
+
| # | Where | The disagreement | Resolution |
|
|
64
|
+
| --- | --- | --- | --- |
|
|
65
|
+
| 1 | Appendix A vs §5.8 | Digests are written `"sha256:..."` in Appendix A and `"..."` in §5.8. | One canonical form, `sha256:<64 lowercase hex>`. Two notations for one value hash two ways, giving one piece of content two envelope hashes, two `caseDedupKey`s (§7.3), two cases and two penalties for one incident. Appendix A is the reference document, so its notation wins and the bare form is rejected rather than normalised. |
|
|
66
|
+
| 2 | Appendix B vs §10.7 | `recommendedActions` is a list of objects (`{action, targetResourceIds}`) in Appendix B and a list of bare strings in §10.7. | Objects on a decision, strings on a review. Appendix B is the reference Decision; a decision that recommends removal without naming what to remove is not actionable. §9.3 writes a review's recommendations as strings and that is kept. `reference-documents.test.ts` pins the §10.7 divergence so widening the contract to accept it has to be a deliberate act. |
|
|
67
|
+
| 3 | Appendix B vs §11.6 vs §6.4 | The universal taxonomy version is spelled `taxonomy`, `universal` and `universalTaxonomyVersion` in three places. | Each surface keeps the spelling of its own reference payload: `DecisionPolicyVersions` uses `taxonomy`, `ReputationPolicyVersions` uses `universal`. Both are reference documents in the approved plan and rewriting either here would be a silent edit. **Recommended follow-up:** unify on `{taxonomy, application, oxyConduct}` when the event contract is agreed with OxyHQServices. |
|
|
68
|
+
| 4 | §5.3 vs §5.8 | §5.3 lists `dimensions` as minimum data for an image; §5.8's own image resource omits them. | Optional. §5.3's "minimum data" is guidance and its own examples contradict it. Duration IS required for video and audio, where no example contradicts §5.3. |
|
|
69
|
+
| 5 | §5.2 vs §5.3 | §5.2 makes `language` apply "where applicable"; §5.3 lists it as minimum data for text. | Optional. §5.2 is the field definition and text of unknown language is real; making it required would push applications into guessing. |
|
|
70
|
+
| 6 | §5.5 | `authored_by` relates a resource to a principal, but the id spaces are separate and no example exercises it. | `authored_by` resolves `to` against `principalBindings`; every other relation resolves it against `resources`. Pinned by a test. |
|
|
71
|
+
| 7 | §5.1 vs §5.8 vs Appendix A | Which root keys are required. | Required: `schemaVersion`, `applicationId`, `externalReportId`, `subject`, `principalBindings`, `resources`, `relations`, `allegations`, `policy`, `privacy`. Optional: `source`, `urgency`, `metadata` — §5.8 omits exactly those three and Appendix A carries all of them. `resources` and `allegations` additionally need at least one entry. |
|
|
72
|
+
| 8 | §9.6 | Enumerates decision outcomes; review outcomes are never enumerated. | Review outcomes are narrower: `violation`, `no_violation`, `insufficient_context`, `content_unavailable`. `inconclusive` is what the engine reports when a panel does not agree — a reviewer cannot fail to agree with themselves, and "the absence of consensus is neither guilt nor innocence" only holds if it is never voted for. `duplicate` and `escalated` are case states. |
|
|
73
|
+
| 9 | §11.6 vs §5.1 | The envelope's `principalBindings` always show a `bindingProofId`, but non-Oxy principals have no Oxy identity to prove. | Required when `type` is `oxy_user`, optional otherwise. That keeps "no binding proof, no Oxy Trust effect" structural without locking out every tenant whose users are not Oxy users. On the reputation event, `subject.bindingProofId` is required unconditionally (§11.7.4). |
|
|
74
|
+
| 10 | §12.4 | Proposes sandbox + staging + production. | `source.environment` is `production` or `sandbox`. CrowdSource deploys once; sandbox is an application-trust state inside it, per this repository's `AGENTS.md`. |
|
|
75
|
+
| 11 | Appendix F vs §5.1 | `applicationId` "comes from the credential, never from the request body", yet every example envelope carries one. | Kept and required, documented as an assertion to be COMPARED with the credential-derived id and rejected on mismatch. The contract cannot enforce this — **the ingress route must**, and it is the highest-risk invariant in the package. |
|
|
76
|
+
|
|
77
|
+
### Values invented rather than quoted
|
|
78
|
+
|
|
79
|
+
Three tokens do not appear anywhere in the plan and are named here. They are
|
|
80
|
+
listed separately because they are the places a product decision could still
|
|
81
|
+
overrule this package.
|
|
82
|
+
|
|
83
|
+
- **`application_local`** (`FindingScope`). §11.7.5 lets only `oxy_network` and
|
|
84
|
+
`identity_integrity` reach Oxy Trust, so the complement must exist and be
|
|
85
|
+
nameable — §6.5's whole argument is that a local restriction does not become a
|
|
86
|
+
global sanction. Modelling it as an absent field instead would make §11.7.5 a
|
|
87
|
+
presence check, which fails open.
|
|
88
|
+
- **`local_user`, `organization`, `bot`, `federated_actor`** (`PrincipalType`).
|
|
89
|
+
§3 defines a principal as "an Oxy user, a local user, an organization, a bot or
|
|
90
|
+
a federated actor" but only ever writes `oxy_user`. The concepts are the
|
|
91
|
+
plan's; the tokens are this contract's.
|
|
92
|
+
- **`conflict_of_interest`, `language`, `too_sensitive`,
|
|
93
|
+
`insufficient_context`** (`RecusalReason`). §4.1 lists exactly these four
|
|
94
|
+
grounds in prose and §10.3 asks for a "structured reason".
|
|
95
|
+
|
|
96
|
+
### Left open on purpose
|
|
97
|
+
|
|
98
|
+
- **`sensitivityHint`** and **`urgency.hint`** are bounded lowercase tokens, not
|
|
99
|
+
enums. The plan names one value each (`standard`, `normal`) and §7.5 clearly
|
|
100
|
+
implies more. Both are HINTS: the authoritative `sensitivity_class` and
|
|
101
|
+
priority are computed server-side by triage (§7.4, §12.8), and access to
|
|
102
|
+
sensitive material is gated on the computed class, never on what a tenant
|
|
103
|
+
asserted. Closing either list is a product decision that has not been made.
|
|
104
|
+
- **The remaining reputation event types.** §11.5 names four bridge operations
|
|
105
|
+
(apply, finalize, reverse, reconcile) but §11.6 specifies only
|
|
106
|
+
`moderation.decision.finalized.v1`. The others are not invented here;
|
|
107
|
+
`ReputationEventSchema` is a discriminated union of one so adding them is
|
|
108
|
+
additive and consumers already switch on `type`.
|
|
109
|
+
- **Size limits.** §7.2.3 requires ingress to bound sizes and resource counts
|
|
110
|
+
but states no numbers. `CONTRACT_LIMITS` declares them — generous enough not to
|
|
111
|
+
reject real material, finite so nothing in the contract is unbounded. A tenant
|
|
112
|
+
may be held to something tighter by quota.
|
|
113
|
+
|
|
114
|
+
## Tests
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
bun run --cwd packages/contracts test # vitest, valid and invalid examples
|
|
118
|
+
bun run --cwd packages/contracts lint # tsc for src/ and again for the tests
|
|
119
|
+
bun run --cwd packages/contracts build # tsc → dist/ (CommonJS + .d.ts)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`src/__tests__/fixtures/` holds Appendix A, Appendix B, §5.8 and §10.7 verbatim,
|
|
123
|
+
and `reference-documents.test.ts` parses them. Where a reference document does
|
|
124
|
+
not validate as written, the test names the exact tokens responsible and the
|
|
125
|
+
suite asserts that nothing else in the document is refused.
|
|
126
|
+
|
|
127
|
+
Every negative test asserts the issue PATHS, not merely that parsing failed — a
|
|
128
|
+
negative example that fails for an unintended reason passes just as loudly as a
|
|
129
|
+
correct one, and then stops testing anything the day its rule is removed.
|