A standalone AT Protocol labeler: a self-hostable, backend-agnostic alternative to Ozone designed to work with Coop
troposphere docs src ingesting-content.md
3.4 kB · 70 lines
Markdown
at main

Ingesting content into Coop #

For Coop to moderate AT Protocol content, that content has to arrive as Coop items. troposphere's ingestion worker streams Jetstream and posts records to Coop's item intake over HTTP, so Coop can run rules and route content to review queues. This is the proactive-detection path; user reports arrive separately (see Receiving reports).

Coop itself never connects to Jetstream and needs no atproto-specific connector. This worker does that job and posts plain items to /items/async, which is why Coop stays generic. If your Coop shows no atproto content, it is not because Coop lacks a firehose; it is because this worker is not running or is scoped to different accounts.

Ingestion runs as its own process, opt-in, so a troposphere deployment that only labels does not pay for it.

Running it #

COOP_ITEMS_URL=https://your-coop/api/v1/items/async \
COOP_ITEMS_API_KEY=<coop org API key> \
npm run ingest

The worker connects to Jetstream, maps each record to a Coop item, batches, and POSTs { items: [...] } to /api/v1/items/async with X-API-KEY. It reconnects with backoff and maintains a live tail.

Configuration #

Variable Default Meaning
COOP_ITEMS_URL (required) Coop's /api/v1/items/async endpoint.
COOP_ITEMS_API_KEY (required) Coop org API key, sent as X-API-KEY.
JETSTREAM_URL wss://jetstream2.us-east.bsky.network/subscribe Jetstream instance.
JETSTREAM_COLLECTIONS app.bsky.feed.post,app.bsky.actor.profile Record types to stream.
JETSTREAM_WANTED_DIDS (all) Restrict to specific accounts.
INGEST_BATCH_SIZE 50 Max items per POST.
INGEST_BATCH_INTERVAL_MS 1000 Flush interval.
COOP_POST_TYPE / COOP_ACCOUNT_TYPE ATproto-post / ATproto-account Coop item type ids.

Scope your ingestion #

The full firehose is enormous. Ingesting every post on the network into Coop is almost never what you want. Scope it:

  • JETSTREAM_WANTED_DIDS restricts to your project's accounts.
  • JETSTREAM_COLLECTIONS restricts to the record types you moderate.

For a project running its own PDS or AppView, point JETSTREAM_URL at a Jetstream fed by your own infrastructure, or filter to your accounts.

Item types and custom lexicons #

The worker maps app.bsky.feed.post to your post item type and app.bsky.actor.profile to your account item type. Those item types must already exist in your Coop org (created there once), and COOP_POST_TYPE / COOP_ACCOUNT_TYPE must be set to their item type IDs (the id Coop assigns each type, not the display name). Coop's HTTP intake matches typeId against the id and rejects the batch otherwise. The mapping produces the same item shape a reported post gets, so ingested and reported content look identical to Coop.

Mapping additional or custom record types (your own lexicons) to Coop item types is not built in yet; today the worker covers posts and profiles. See the roadmap.

What it does not do #

Ingestion only submits items. Whether an item reaches a reviewer is up to Coop's rules and routing (unlike reports, which Coop always enqueues). This is intentional: the item path is for proactive detection and automated enforcement, so you decide in Coop which content warrants review.