Deploying with Coop
Coop is the moderation tool; troposphere is its AT Protocol
side. Coop is where you set up your own policies, rules, and actions and where your reviewers work
(the role Ozone’s UI plays); troposphere is the plumbing Coop uses to publish labels, receive
reports, and enforce takedowns. Coop does not come with policies or reviewers of its own, and it
holds no atproto-specific code; those are yours, and troposphere’s, respectively.
AT Protocol content ─▶ Coop (your policies, rules, actions; your reviewers)
│ decision (label / take down)
▼
troposphere ─▶ signed labels ─▶ subscribeLabels / queryLabels
▲ ─▶ takedown ─▶ your PDS
│
inbound report ───────────┘ (createReport, forwarded into a Coop queue)
What each side owns
- Coop: the tool where you configure policies, rules, and actions, review queued items, and act on them. Its actions fire outbound HTTP callbacks. The reviewers and policies are yours.
- troposphere: ingests content into Coop, signs and serves labels, accepts and forwards reports, and enforces takedowns. It holds the labeler signing key; Coop never sees it.
1. Deploy troposphere
Follow Getting started. For a Coop deployment you will typically set:
LABELER_DID=did:web:labeler.yourorg.example
LABELER_SIGNING_KEY=...
ADMIN_TOKEN=... # Coop presents this on label/enforce calls
DATABASE_URL=postgres://.../troposphere
REPORT_FORWARD_URL=... # Coop's report/item intake (see step 4)
REPORT_FORWARD_TOKEN=... # Coop API key
PDS_URL=https://your-pds # only if you enforce takedowns on your own PDS
PDS_ADMIN_PASSWORD=...
Then publish your labeler identity.
2. Labels and takedowns out: Coop action to troposphere
Coop publishes a label or takedown by firing a CUSTOM_ACTION at a reviewer’s decision (or from a
rule). Point it at troposphere’s Coop endpoint, POST /coop/action, which accepts Coop’s action
shape directly, with no adapter:
- callbackUrl:
POST /coop/actionon troposphere. - callbackUrlHeaders:
{ "Authorization": "Bearer <ADMIN_TOKEN>" }. - callbackUrlBody: the directive.
{ "create": ["spam"] }labels,{ "negate": ["spam"] }removes a label,{ "takedown": true }takes the content down. Combine them in one action if you want:{ "create": ["nsfw"], "takedown": true }.
Coop injects the item’s atproto URI as item.id; troposphere reads it as the subject and, for a
record, resolves the current CID from the record’s own PDS so the label or takedown pins to that
version. You do not compute the CID in Coop. Takedowns require PDS_URL/PDS_ADMIN_PASSWORD (your
own PDS); see Enforcement.
3. Ingestion and review
Run troposphere’s ingestion worker to pump atproto content into Coop:
COOP_ITEMS_URL=https://your-coop/api/v1/items/async \
COOP_ITEMS_API_KEY=<coop org API key> \
JETSTREAM_WANTED_DIDS=<your accounts> # scope it; the full firehose is huge
npm run ingest
Coop runs rules over the items and routes them to queues. A reviewer opens an item, sees the post and its author context, and picks an action; a label or takedown action fires the callback in step 2. None of this involves Ozone. See Ingesting content into Coop.
4. Reports in: troposphere to a Coop queue
Turn on the Coop report format and point troposphere at Coop’s report intake:
REPORT_FORWARD_FORMAT=coop
REPORT_FORWARD_URL=https://your-coop/api/v1/report
REPORT_FORWARD_TOKEN=<coop org API key> # sent as X-API-KEY
COOP_POST_TYPE=<post item type id> # the org's item type IDs, not the display names
COOP_ACCOUNT_TYPE=<account item type id>
When a user reports content to your labeler, troposphere verifies and enriches the report (see
Receiving reports), maps it onto Coop’s POST /api/v1/report envelope
(reporter as a user report keyed by the reporter DID, the reported record or account as the
reportedItem, and the reason on reportedForReason), and posts it with X-API-KEY. Coop’s
report endpoint always enqueues the item to a review queue, so the report lands in Coop alongside
everything else. No adapter needed.
Integration status
Both directions are turnkey and tested end to end:
- Labels and takedowns out:
/coop/actionaccepts Coop’sCUSTOM_ACTIONbody as-is, so wiring is just configuring a Coop action (step 2). No adapter. - Reports in:
REPORT_FORWARD_FORMAT=coopposts Coop’s/api/v1/reportenvelope withX-API-KEY(step 4). No adapter.
One thing remains, tracked as “Coop wiring” on the roadmap: the Coop-side action configuration itself (creating the label and takedown actions and attaching them to queues in Coop), and optionally generating the labeler declaration from Coop’s configured labels.