@atproto/ozone

中文文档
0.12.3发布于 2026/11/06MITTypeScriptES Module构建来源已验证近 30 天下载 3.9万

Backend service for moderating the Bluesky network.

原始使用文档
文档目录11 节
  1. DID cache
  2. Blob diversion
  3. Report tags
  4. Closing reports with moderation actions
  5. Wildcard label queries
  6. Historical label replay
  7. Moderation query cancellation
  8. Moderation inbox standing
  9. Moderation inbox start date
  10. Strike expiry
  11. License

@atproto/ozone: Bluesky Moderation Service

Backend service for moderating the Bluesky network.

NPM Github CI Status

DID cache

The API uses a bounded in-memory DID document cache. Configure its limits with OZONE_DID_CACHE_MAX_ENTRIES (default 10,000), OZONE_DID_CACHE_MAX_BYTES (default 33,554,432 bytes for serialized documents and DID keys), and OZONE_DID_CACHE_MAX_DOC_BYTES (default 65,536 bytes per serialized document). Set any limit to 0 to disable retention. Documents exceeding a limit can still be resolved but are not cached. Restart the API after changing these settings.

Blob diversion

OZONE_BLOB_DIVERT_MAX_BYTES sets the maximum size of a blob downloaded for diversion. It must be a positive integer and defaults to 314,572,800 bytes (300 MiB). The receiving service must support the same size. Restart the API after changing this setting.

Report tags

Reports automatically add a reason tag such as report:spam to their subject by default. Set OZONE_REPORT_TAGS_DISABLED=true and restart the API to disable these automatic report tags. Unset or false keeps them enabled.

The setting applies to reports submitted through both com.atproto.moderation.createReport and tools.ozone.moderation.emitEvent.

Language and embed tags, manually applied tags, and existing report tags are unaffected.

Closing reports with moderation actions

tools.ozone.moderation.emitEvent accepts reportAction: { allOwned: true } on an account event that closes reports. It links the event to eligible reports on the account and its records, messages, and conversations. allOwned targets all report types; explicit ids also include those reports even when they are assigned or escalated. The event and closures are committed together.

tools.ozone.report.closeReports accepts an existing actionEventId. The event must be a report-closing event on the requested subject or its owning account; includeAllOwned requires an account event. The selected reports are linked to that event and closed in one transaction. Without an event ID, closures retain their no-action outcome. Both APIs protect assigned, escalated, and actively assigned reports from broad selection. closeReports can additionally filter or exclude report types.

For a message event, matching reports must also have the event's conversation ID. This keeps messages with the same ID in different conversations separate.

The linked event may predate a report. The resulting action attribution refers to that event, which may no longer be in effect. An event that predates an appeal, or an account event linked to an appeal about another subject, does not determine the appeal's outcome: appeal outcomes require a later event on the appealed subject.

Wildcard label queries

Wildcard patterns in com.atproto.label.queryLabels are disabled by default, including * and requests mixing wildcard and exact patterns. They return HTTP 400 (InvalidRequest) with the message "Support for wildcard label queries is temporarily disabled" before accessing the database. Exact URI queries remain available.

Set OZONE_LABEL_QUERY_WILDCARDS_ENABLED=true and restart the API to restore wildcard support. Unset or false disables it.

Historical label replay

com.atproto.label.subscribeLabels connections with a cursor share a historical replay budget on each API process. OZONE_LABEL_STREAM_BACKFILL_RATE sets the positive integer number of labels per second; the default is 2000. Pages contain at most 500 labels, so brief page-sized bursts are possible. Restart the API after changing the setting.

This bounds bulk replay work while preserving history and cursor ordering. Large backfills take longer, and multiple replaying clients share the budget. Live subscriptions and sequencer polling do not wait for this budget; cutover queries remain bounded by the existing stream buffer limit.

Subscription start and close logs include a connection ID, cursor, proxy socket address/port, User-Agent and forwarded address chain. Close logs add the last sequence, yielded event count and duration. Forwarded headers and User-Agent are attribution hints, not authenticated caller identities; cookies and authorization headers are excluded.

Moderation query cancellation

queryEvents and queryStatuses cancel their listing SQL when the HTTP client disconnects, using Kysely's cancel query abort strategy. Kysely uses a separate PostgreSQL connection for cancellation and holds the listing connection until SQL and cancellation finish. This adds no query deadline.

Moderation inbox standing

Set NEXT_PUBLIC_STRIKE_SUSPENSION_CONFIG to the same value used by Ozone UI, for example 4:72,8:168,12:336,16:Infinity. Each entry maps a strike count to suspension hours; Infinity represents a permanent suspension. The backend retains the full map and uses the second and third thresholds, sorted by strike count, for warning and atRisk. The first tier does not change standing. With no configuration, standing derives from the account's enforcement and restrictions; strike thresholds are disabled. Invalid entries fail configuration at startup.

Moderation inbox start date

Set OZONE_INBOX_START_AT on both the API and daemon to an ISO 8601 timestamp with a timezone, for example 2026-10-15T00:00:00.000Z. Configuration normalizes it to UTC milliseconds and rejects invalid values at startup. Unset exposes all history. A future date keeps history empty until eligible activity occurs; changing the value requires restarting the services.

Reports are visible only when their createdAt is at or after the date, even if an older report is later resolved or reopened. The same bound applies to report detail and resolution actions. Subjects require a public action created at or after the date; action counts, first-action dates, latest actions, detail history and report summaries exclude earlier history. Older appeal history is hidden, while eligibility still respects earlier appeals. Inbox appeals cannot target hidden actions or subjects. Moderator previews use the same cutoff.

Account standing and current enforcement remain live: active restrictions, strikes and labels can predate the inbox start. Preferences and seen watermarks do not expose history; they retain their existing behavior. The cutoff is not a read watermark, so an item created exactly at the start date is initially unread.

Notification producers skip activity before the date and skip transitions on reports or appeal subjects that would be hidden. Lists and unread counts independently enforce the date and target visibility, including existing stored notifications. API and daemon must use the same value. Source reports/actions continue to be recorded normally. Moving the date earlier can reveal more source history, but skipped notifications are not backfilled. Complete the writer rollout before the chosen start date to avoid a notification gap.

Report-first pagination requires the reporterDid backfill and the post-retirement catch-up sweep to finish before deploying this reader. Pre-create the reporter/time indexes and idx_moderation_event_inbox_public_created concurrently in production before the inbox migration. The latter indexes (subjectDid, createdAt DESC, id DESC) so subject history can seek the configured date.

Strike expiry

Clients supply strike amounts and expiry timestamps from severity settings. The backend stores them and excludes expired strikes from active totals.

Legacy events without expiry require a separate repair. Changing expiry does not undo takedowns, suspensions, or lifetime strike totals.

License

This project is dual-licensed under MIT and Apache 2.0 terms:

Downstream projects and end users may chose either license individually, or both together, at their discretion. The motivation for this dual-licensing is the additional software patent assurance provided by Apache 2.0.

版本与发布记录

@atproto/ozone 共发布 269 个版本,首次发布于 2024/01/19;过去 12 个月发布了 93 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.12.3latest2026/11/061 天前
0.0.0-spaces-alpha-20261105160032alpha2026/11/062 天前
0.12.02026/10/317 天前
0.0.0-spaces-alpha-202610291719132026/10/309 天前
0.11.02026/10/2810 天前
0.10.102026/10/2711 天前

其他发布通道:rc → 0.1.36-rc.1,next → 0.1.175-next.0,alpha → 0.0.0-spaces-alpha-20261105160032。

关于 @atproto/ozone 的常见问题

@atproto/ozone 是什么?

Backend service for moderating the Bluesky network.

如何安装 @atproto/ozone?

在项目目录运行 npm install @atproto/ozone,或使用 pnpm add / yarn add / bun add。

@atproto/ozone 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@atproto/ozone 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@atproto/ozone 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@atproto/ozone 可以商用吗?

@atproto/ozone 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@atproto/ozone 的下载量有多少?

根据 npm 官方下载统计,@atproto/ozone 在 2026/10/07 – 2026/11/05 这 30 天内被下载 39,202 次,日均约 1,307 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@atproto/ozone 还在维护吗?

最新版本 0.12.3 发布于 2026/11/06(1 天前);至今共发布 269 个版本,其中过去 12 个月发布了 93 个;首次发布于 2024/01/19。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。