Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Coop台灣繁體中文指南

ROOST 開放原始碼工具 × 台灣繁體中文

讓小型社群,也能開始建立可被檢查的安全治理流程

Coop 將自動規則、人工審查、檢舉、申訴與稽核集中在同一套開放原始碼工具。這份繁中指南協助社群管理者、志工與工程團隊理解流程,再判斷哪些部分適合自己的平台。

  • 37 / 37上游文件完成第一輪翻譯
  • 4 道檢查用語、忠實度、建置與連結
  • 版本可追溯對應固定的英文來源版本
Coop 內容治理工作台
Coop 儀表板,顯示處置總數、等待審查工作、自動與人工處置比例及常見政策違規
規則、審查 Queue 與治理指標維持在自己管理的基礎設施中。

適合拿來開始評估

  • 志工型社群與論壇
  • 小型內容平台
  • 公民科技專案

ROOST 安全工具生態系

從偵測到執行,看見完整治理流程

不同規模的社群可以從目前最需要的環節開始。DIRE 將安全工作拆成偵測、調查、審查與執行,讓工具選擇回到實際責任與流程。

  1. D
    Detection偵測

    安全模型、雜湊比對與平台 Signals 找出需要注意的內容或行為。

  2. I
    Investigation調查

    Osprey 協助分析事件、關聯實體與協同行為,再建立可重複使用的規則。

  3. R
    Review審查

    Coop 將內容送到適當 Queue,提供脈絡並保存人工判斷。

  4. E
    Enforcement執行

    自動規則或人工決定透過 Action 回到平台,並保留申訴與稽核紀錄。

使用提醒 工具或模型被收錄不代表 ROOST 或繁中維護者背書。採用前仍需核對維護狀態、授權、資料處理方式、語言涵蓋率與適用條件。

依角色開始

從眼前需要處理的問題進入

不必先讀完所有技術文件。可依目前負責的治理工作,選擇最接近的入口。

完整治理迴路

一套工具,串起決策前後的責任

Coop 的價值涵蓋自動化、人工判斷、對外處置、申訴與稽核,不只提供內容分類結果。

  1. 01
    接收內容與檢舉

    平台提交 Items,使用者 Reports 帶入需要處理的脈絡。

  2. 02
    規則評估與路由

    Signals 與 Rules 協助自動處理,或將工作送入適當 Queue。

  3. 03
    人工審查與照護

    內容審查員取得必要脈絡,並使用身心健康功能降低暴露風險。

  4. 04
    執行 Action

    透過 callback 將警告、限制或其他治理決策送回平台。

  5. 05
    申訴與稽核

    保存決策紀錄、處理 Appeals,讓結果可追蹤且可被複核。

可驗證,不只可閱讀

每次更新都經過同一組自動檢查

繁中內容以來源清單追蹤英文版本,並用單一指令檢查翻譯結構與產生後頁面。自動化結果不會取代人類的語言、法律或領域判斷。

查看完整工作流程
  • 來源狀態

    37 份 Markdown 對應固定 commit 與明確審查狀態。

  • 台灣繁體中文

    攔截簡體詞候選、語氣混用與已完成頁面的舊英文連結。

  • 技術忠實度

    核對 code blocks、inline tokens、圖片、表格與 style blocks。

  • 可用頁面

    完成 mdBook build,再檢查內部頁面、錨點與靜態資源。

使用前請留意

工具與翻譯都不能代替組織責任

NCMEC 是美國制度;文件內容不構成台灣法律意見,也不代表平台已完成權限、資料保存、事件應變、申訴及正式部署驗證。任何兒少安全整合測試都不得使用真實 CSAM、私密影像或可識別個案資料。

開始建立共同語言

先理解治理元件,再決定要自動化多少

從基本概念認識 Coop 的資料模型與流程,或直接查看完整目錄。

Coop

用自己的方式進行審查與內容治理。

Coop 概覽,顯示已採取的處置總數、等待審查的工作、自動與人工處置比例,以及最常見的政策違規

Coop 是 ROOST 推出的開放原始碼審查與內容治理工具,為網路安全提供完整解決方案。

  • 審查主控台:供人工處理複雜內容治理決策的介面
  • 內容處理:支援貼文、留言、媒體與自訂內容類型
  • 分析:提供內容治理成效與趨勢的詳細資訊
  • 規則引擎:依照可自訂的政策自動評估內容
  • API 整合:提供簡易 REST 與 GraphQL API,與平台順暢整合

Coop 適用對象

Coop 適合任何需要作成網路安全決策的人,包括各種規模的平台、獨立開發者,以及沒有專職信任與安全人員的社群團隊。

多數內容治理工具採專有授權,定價也以原本就負擔得起的平台為對象。Coop 免費且開放原始碼,讓資料保留在自己的基礎設施中,也能依社群需要自訂。

下列原則影響 Coop 的開發方式。

  • 平台擁有自己的政策。 Coop 提供實作及執行自有規範所需的管線。
  • 兒少安全是優先工作流程。 Coop 以成為第一套免費、端對端的網路兒少安全系統為目標,這也是專案存在的原因之一。
  • 程式碼可供稽核。 沒有隱藏邏輯,也不會被單一供應商綁定。

正式環境採用情形

Coop 由下列組織使用。

KyodoNotionMusubi

正在使用 Coop,並希望把專案或組織加入清單嗎?請建立 pull request

公開開發

Coop 是持續積極開發的開放原始碼專案。功能與文件會依社群回饋演進。

無論正在測試、遇到問題,或有改善想法,都歡迎建立 issue加入或發起 discussion,或加入 Discord

您的回饋會直接影響 ROOST 的專案路線圖

快速開始

使用 Docker Compose,以單一指令執行 Coop。設定方式與已發布映像檔的詳細資訊,見 Docker 指南

深入了解

繁中版包含使用者指南開發指南API 參考整合資訊。英文來源可從 Coop 官方文件網站取得。

台灣使用提醒

「資料保留在自己的基礎設施中」以自行託管與正確設定為前提。外部 signals、API 整合、備份、紀錄與部署方式仍可能把資料傳送到其他服務或地區,導入時需逐項確認。

文件入口

歡迎閱讀 Coop 文件。原始碼與專案資訊見 GitHub 上的 Coop

文件依讀者身分與需求分成幾份指南。

請注意,官方文件有版本區分。其他版本可從官方文件網站首頁取得。本繁中版本則以 sources.tsv 記錄各頁所依據的英文 commit。

貢獻

Coop 是由 ROOST 與社群共同建立的開放原始碼專案。我們歡迎各種貢獻,也需要多元觀點與專業能力,一起建立更安全的網路空間。

若您剛接觸本專案,建議先進行下列事項。

撰寫程式碼不是協助專案的唯一方式。審查 pull request、回答問題、提供回饋、籌辦或教授教學課程,以及改善文件,都是十分珍貴的貢獻。

回報問題

發現錯誤或有功能需求時,歡迎告訴我們。

尋求協助

若需要 Coop 使用協助或希望取得聯繫,隨時歡迎採取下列方式。

使用者指南

用簡明的方式保護使用者免受傷害。

Coop 概覽

Coop 是 ROOST 推出的開放原始碼審查與內容治理工具,為網路安全提供完整解決方案。

  • 自動處置:可自訂的多條件規則,依 signals 評估每個提交的 Item,並根據您的政策自動採取行動,或將檢舉送到人工審查 Queue

    規則詐騙規則
    政策文字資料庫
  • 審查主控台:可設定的人工審查 Queue,讓內容審查員快速作成複雜且依政策判斷的治理決策,並內建額外脈絡與身心健康功能

    審查主控台工作頁面
    審查主控台設定身心健康設定
  • 整合豐富內容處理:支援審查及比對文字內容與討論串、圖片與影片媒體、帳號資料及自訂內容類型。可使用內建 signals,以及 Google、OpenAI、Zentropi 與 NCMEC 等外部服務

    整合Items
    NCMEC 工作HMA 路由
  • 指標與報告:提供資訊儀表板與詳細稽核紀錄,以利問責,並了解內容治理的成效與趨勢

    資訊儀表板近期決策
  • API 整合:簡易 REST 與 webhook API,可讓平台雙向整合內容匯入、使用者檢舉、申訴、執行內容治理動作、取得其他 Item 等功能

    動作API 金鑰
  • 其他功能。 Coop 還支援 NCMEC 通報、雜湊比對、使用者違規次數、申訴、調查、批次處置、完整的使用者與角色管理、依地點處置與單一登入等功能。由於專案採開放原始碼並支援自訂整合,也具備廣泛的調整空間。

Coop 如何運作

下列簡圖可協助理解資料如何在平台與 Coop 之間流動。

簡化資料流程圖

詳情請參閱基本概念技術架構API 參考

管理員入門

建議先熟悉 Coop 的繁中版基本概念。了解後,依序完成下列設定。

  1. 確認您有 Coop instance 的帳號與 API 金鑰
  2. 定義 Item Types,也就是平台上的內容與行為者類型
  3. 輸入詳細的平台政策
  4. 定義動作,並提供 callback 端點,讓 Coop 可觸發平台上的處置

Coop 設定完成後,平台可進行下列操作。

  1. 透過 Items API 將 Item 提交至 Coop,套用主動式規則
  2. 透過 Report API 提交使用者檢舉,將內容送入審查員使用的 Queue

台灣使用提醒

外部服務的「免費」方案、資料用途與使用條件可能變動。涉及內容、帳號、媒體、地理位置或兒少安全資料時,應先確認資料流向、保存期間、權限與契約條款。NCMEC 是美國制度,本指南提及其功能不代表台灣平台的通報義務或法定程序。

基本概念

以下是 Coop 的核心構成要素。了解這些概念,能協助您快速開始並建立工作流程。理解後,應能完成 Coop 設定,並開始使用各項功能。

這些概念依照建立 Coop 設定時的建議順序排列。 部分概念建立在前面的概念上,因此建議依序讀完。

Item

Item 是平台上的任何實體,可包含單項內容,例如貼文、留言、私訊、商品刊登與商品評論;內容討論串,例如留言串與群組聊天;或使用者及其個人檔案。即使一個實體內含其他 Item,仍可將其視為獨立 Item。

Item Type

Item Type 代表平台上的不同 Item 類型。例如,社群網路可能有 ProfilePostCommentComment Thread。市集平台可能包含 BuyerSellerProduct ListingProduct ReviewDirect MessageTransaction 等。傳送至 Coop 的每個 Item,都必須只屬於其中一種 Item Type。

設定流程的第一步,是在 Coop 的 SettingsItem Types定義 Item Types

Item Type 的類別

Item Type 是通用概念,可代表平台上的任何項目,從單獨的內容、使用者及其個人檔案,到包含多項相關內容的討論串皆可。

為了讓 Coop 針對不同 Item Type 提供更實用的功能,Item Type 分成三類。

  1. Content:單項內容,例如訊息、留言、貼文、商品刊登與評論等

  2. User:平台上的個別使用者。部分平台只有一種 User Item Type,也有平台會有多種。例如,市集可能把買家與賣家設為不同的 User Item Type,共乘服務也可能把駕駛與乘客設為不同類型

  3. Thread:依順序排列的內容清單。例如包含大量訊息的群組聊天、包含大量貼文的留言板,以及包含大量留言的留言串。這些都屬於按指定順序包含多項內容的 Thread

Coop 會以不同方式處理及顯示這些 Item Type,因此建立每種 Item Type 時,Coop 都需要知道它屬於三類中的哪一類。

Item Type Schema

Schema 代表 Item Type 的資料形狀。例如,平台上的 Profile 若包含使用者名稱、個人圖片、簡介與興趣清單,Coop 就需要知道這些資訊,才能在規則中引用資料,並於 Coop 使用者介面中正確顯示。

每個 Item Type Schema 都由 Field 清單構成,每個 Field 代表 Schema 中的一項資料。前述 Profile Item Type 的 Schema 可能包含下列 Fields。

  • usernamestring
  • profile_pictureimage
  • biostring
  • interestsArray<string>

您可以加入所需數量的 Field,再於規則中使用。

Coop 如何唯一識別 Item 的重要說明

在 Coop 中,使用(Item ID, Item Type ID)組合唯一識別特定 Item。有些平台無法保證留言 ID 與使用者 ID 不會重複,也有平台營運者同時擁有及經營多個平台,無法保證不同平台的 Item ID 不會互相重複。

在這些情況下,需要使用(Item ID, Item Type ID)組合,才能唯一識別正確的 Item。在 API request 中,兩者表示為同層欄位 idtypeId。建議以下列結構傳送 Item。

item: {
  id: string;
  typeId: string;
}

id 欄位是平台對該 Item 使用的唯一識別碼,typeId 則是 Coop 為對應 Item Type 產生的 ID。在 Coop 資訊儀表板建立 Item Type 後,便會看到產生的 ID。向 Coop 傳送 API request 時,可用它填入 typeId 欄位。

Actions

Coop 中的 Action 代表任何可對 Item 執行的動作。常見的信任與安全範例包括 DeleteBanMuteSend to Moderator。也可以加入非信任與安全用途的動作,例如 PromoteAdd to TrendingMark as TrustworthyApprove Transaction。任何自動化動作都能加入 Coop。

Action 會顯示在 Proactive Rules 中,供符合條件的 Item 使用。在 Review Console 中,可用的 Action 會以 Decision 形式提供給審查員,讓審查員針對每個 Job 作成決定。

每個 Action 都對應至組織開放給 Coop 的 API 端點。例如,在 Coop 建立 Delete Action 時,必須提供一個 API 端點,也就是網址,並最好附有驗證機制,讓 Coop 能送出 POST request。如此一來,當 Coop 透過主動式規則或審查員決策觸發 Action 時,便會送出對應的 POST request,實際在平台伺服器上執行該 Action。

設定流程的第二步,是在 Coop 的 SettingsActions 定義這些 Action。

Coop 傳送至 Action API 端點的 webhook payload 詳情,見處理 Actions

Policy

Policy 是平台用來治理使用者行為的一組規則與指引。常見範例包括 SpamNudityFraudHarassmentViolence。更多資訊見 Trust & Safety Professional Association

Policy 可包含子政策。例如,Spam 政策可有 Commercial SpamRepetitive ContentFake EngagementScams & Phishing 等子政策。

將每個 Action 對應至一項或多項特定 Policy 通常很實用,在某些情況下也是必要要求,例如歐盟《數位服務法》。同一則留言可能依 Hate Speech 政策遭到 Delete,也可能依 Spam 政策遭到 Delete。Coop 可分別追蹤這些差異,並計算各 Policy 下採取的 Action 數量。如此可以觀察各 Policy 長期執行成效、辨識處置成效不佳或惡化的 Policy,並向組織管理層或主管機關報告成效指標,例如製作 DSA 透明度報告。

您可以從 Policies 資訊儀表板建立及管理 Policy,也可以透過 Policies API 以程式存取。從 Coop 使用者介面加入的 Policy,也會直接顯示在 Review Console 的 Job 檢視,供審查員查看。

台灣使用提醒 上述《數位服務法》範例描述歐盟制度,不代表台灣平台適用相同義務。即使法律沒有要求,把處置與明確政策依據連結,仍有助於一致性、申訴、稽核與透明度。

Jobs

Report 被送至 Review Console 的 Queue 時,系統會建立 Job。每個 Job 顯示 Report、遭檢舉的 Item,以及 Item 作者的資訊。審查員可以略過 Job,讓它留在 Queue,也可以作成 Decision。Decision 包括 Ignore,也就是不採取行動;Enqueue to NCMEC,前提是已設定 NCMEC;Move 至其他 Queue;以及該 Queue 可用的所有 Action。

Reports

平台使用者標記 Item 時,系統會建立 Report。Report API 用於人工審查,無論來源是使用者標記,或只是為了觸發人工標記流程。平台使用者標記 Item 並由平台傳送至 Report API 後,Coop 會把它送到 Review Console,供內容審查員決定如何處理。

更多資訊見檢舉文件

Appeals

平台使用者不同意內容治理決策時,可能希望「申訴」,也就是要求平台重新查看並確認最初決策是否正確。如果平台支援此功能,Coop 可協助處理完整申訴流程。對部分受歐盟《數位服務法》規範的平台而言,提供申訴是必要要求。

平台使用者要求團隊複核內容治理決策時,可以在 Coop 建立 Appeal。使用者在平台提出申訴後,平台可將申訴要求傳送至 Appeal API。Coop 會將其加入 Review Queue,讓內容審查員決定維持或推翻原始決策。

更多資訊見申訴文件

台灣使用提醒 上述申訴義務範例同樣描述歐盟制度。台灣社群仍應依服務類型、契約、社群規範與適用法令,確認申訴、通知及救濟安排。

自動處置與路由

Coop 可透過主動式規則自動對提交的 Item 執行 Action,並透過路由規則將 Report 送至正確的審查 Queue。使用者反覆違反 Policy 的情況,則由使用者違規次數管理。

Rules

兩種 Rule 都由相同構成要素建立,包括引用 Signals比對資料庫的條件。每個 Rule 適用於一種或多種 Item Type,因此可為貼文及留言建立文字規則、為圖片及影片建立雜湊比對規則,或為附有地理中繼資料的使用者提交內容建立地點規則。

Rule 條件可設為 AND,表示所有條件都必須符合;也可設為 OR,表示任一條件符合即可。

主動式規則

Proactive Rules(主動式規則)會自動執行處置。Item 提交至 Coop 時,系統會逐一評估所有啟用的 Proactive Rule,並自動執行每個符合 Rule 所設定的 Action。請至 Automated EnforcementProactive Rules 設定。

主動式規則

凡是符合 Item 的 Proactive Rule 都會觸發,不會只執行第一個。因此,同一項內容符合多個 Rule 時,可能觸發多項自動 Action。

詐騙主動式規則

Proactive Rule 的 Action 可設為 Enqueue Item to Manual Review。系統會將 Item 轉成 Report,再與 Routing Rules 比對並送至正確 Queue。

路由規則

Routing Rules(路由規則)會將新進 Report 送至正確的 Review Console Queue。請至 Coop 的 Review ConsoleRouting 設定。

路由規則

系統會依順序評估 Rule,並將 Report 送至第一個符合 Rule 的 Queue。若沒有符合的 Rule,Report 會進入預設 Queue。每個 Rule 包含一個或多個條件。

HMA 路由規則

比對資料庫

Matching Bank(比對資料庫)是一組可重複使用的值,能供多個 Rule 引用,避免重複輸入。例如,需要在多個 Rule 使用同一份禁用關鍵字時,只需建立一個 Bank,再於需要之處引用。

比對資料庫

文字資料庫

Text Bank 可保存完全相同的詞語、片語或正規表示式清單,用於文字欄位的關鍵字比對與模式偵測。

文字字串資料庫

Coop 也支援變體比對,以找出規避嘗試。例如,比對 hello 時,也可將 h3||0helllllllloooo 視為符合。文字 Signal 類型詳情見 Signals 的文字分析

正規表示式資料庫

雜湊資料庫

Hash Bank 保存已知有害媒體的感知指紋。Coop 搭配 HMA 使用這些資料,將圖片與影片和已知的 CSAM、非自願私密影像(NCII)、暴力極端主義或恐怖主義內容(TVEC),以及自行建立指紋的內容資料庫比對。設定方式見 Hasher-Matcher-Actioner(HMA)

雜湊資料庫

地點資料庫

Location Bank 保存 geohash 或 Google Maps Places 參照清單,可用來對特定地理區域套用不同 Rule,例如在大學校園實施較嚴格的內容政策,或對特定區域採取目標式處置。

地點資料庫

若要使用地點比對,傳送至 Coop 的每個 Item 都需包含 geohash,代表建立內容之使用者的位置。

使用者違規次數

User Strikes 會追蹤平台個別使用者反覆違反 Policy 的情況,讓系統能自動採取逐步升級的回應。您可透過 Policy 與 Action 設定此功能。Coop 會累計違規分數,超過已設定門檻時觸發後續 Action。

User Strikes 概覽,顯示 Policy Scores 分頁、Policy 與各自的違規分數權重。

請至 Automated EnforcementUser Strikes 設定。資訊儀表板有四個分頁。

Policy Scores

每項 Policy 可對使用者違規分數提供不同權重。嚴重違規可能增加 3 分,輕微違規可能增加 1 分。違規分數是設定期間內所有違規的累計總和。

子 Policy 可繼承上層 Policy 的權重,也可自行覆寫。使用 Apply to sub-policies 切換繼承設定。

Strike Enabled Actions

Strike Enabled Actions 分頁列出組織定義的所有 Action,並可切換哪些 Action 執行後會增加使用者違規分數。並非所有 Action 都應計分。例如,「傳送警告」可能不計分,「移除內容」或「停權帳號」則可能需要計分。

Strike Enabled Actions 分頁,列出各 Action 及是否加入使用者違規分數的切換按鈕。

任何應增加分數的 Action,都應開啟 Strike Enabled 欄位。系統會結合前一分頁的 Policy 權重與實際 Action,計算違規分數。

Thresholds & Settings

設定違規紀錄保存多久,以及使用者違規分數超過門檻時要採取的行動。

Thresholds & Settings 分頁,顯示 Strike Window(TTL)及門檻與對應 Action 清單。

Strike Window 控制違規紀錄留在使用者資料上的時間。早於期間的紀錄不列入目前分數。相同數值也可從 Settings → Other → User Strike TTL 編輯。

Thresholds 是超過後會自動觸發 Action 的分數值,可依需要設定多個。例如下列設定。

  • 5 分:將使用者送入人工審查
  • 10 分:暫時限制發布內容
  • 20 分:停權帳號

下拉選單中的 Action 來自組織已定義的 Action

Analytics

此分頁顯示組織內使用者違規分數的分布圖,可在啟用門檻前協助調整。例如,多數活躍使用者分數介於 0 至 2,只有少數位於 8 以上時,將門檻設為 8,可能找出最嚴重的違規者,同時避免影響一般使用者。

Analytics 分頁,顯示組織內使用者違規分數的直方圖。

Signals

Rule 條件實際評估的是 Signal。Signal 接收 Item 欄位並回傳分數,Rule 條件再將分數與門檻比較。詳情見 Signals

台灣使用提醒

  • 多個 Proactive Rule 可同時觸發,因此啟用前應測試 Action 組合是否會重複刪除、重複通知或產生互相衝突的結果
  • 使用者違規分數與門檻屬於平台政策設計,不應只以技術預設值決定。高影響處置應保留理由、通知、人工複核與申訴路徑
  • Hash Bank、Location Bank 與外部 Signals 可能涉及高度敏感資料。導入前應確認資料來源、存取權限、保存期限、跨境傳輸與誤比對處理方式

人工審查與處置

Review Console(審查主控台) 是內容審查員處理遭檢舉內容並作成治理決策的地方。

Queues

五個 Queue 範例,包括媒體、預設 Queue、詐欺、詐騙與垃圾內容。畫面提供建立新 Queue、開始審查、刪除所有 Job,以及編輯或刪除個別 Queue 的按鈕。

Coop 使用 Queue 組織審查 Job。內容遭到檢舉後,無論來源是平台使用者或主動式規則,都會進入 Queue,直到完成審查並作成決策。

內容審查員在任一 Queue 選擇 Start Reviewing 即可開始。Coop 會先取出最早的 Job,作成決策後自動載入下一個,直到 Queue 清空或審查員停止。

兩位內容審查員不會收到同一個 Job,可避免重複工作。

每位使用者可將 Queue 加上星號並固定在 Review Console 頂端。每個 Queue 都會顯示待處理 Job 數量,以協助安排優先順序。

建立與編輯 Queue

內容審查員管理者與管理員可以為組織建立及編輯 Queue。

在 Coop 建立 Queue,顯示名稱與隱藏動作欄位。

建立或編輯 Queue 時,可設定 Reviewer Access,決定哪些內容審查員能存取及處理該 Queue 的 Job。Hidden Actions 則決定哪些 Action 不提供給這個 Queue 的審查員。這適合用來限制特定脈絡下可作成的決策,例如在第一輪分類 Queue 中隱藏永久停權。

路由規則決定新進 Job 要送到哪個 Queue。

Job 檢視

Coop 的審查 Job,使用者資料已遮蔽。畫面顯示收到檢舉的時間、Item 類型、檢舉者與理由、Job 上的檢舉數量及貼文資訊。使用者可加入內部留言、查看政策所列處置指引、選擇決策,或略過 Job。

Queue 中的每個 Job 都會顯示 Report,以及 Coop 所知的遭標記內容或使用者資訊。每個 Job 都有專屬網址,可分享給組織內具有 Coop 存取權的人。

畫面至少會顯示該 Item 已設定的 Fields。依 Item Type 而定,例如貼文、留言、個人檔案或私訊,Coop 也會提供更多脈絡。

  • 與內容相關的使用者帳號
  • 同一使用者的其他內容

Decisions

每個 Job 都會顯示審查員可用的 Decision。預設情況下,每個 Queue 都包含下列選項。

  • Ignore:關閉 Job,不採取任何 Action
  • Enqueue to NCMEC:將 Job 移至 NCMEC 審查 Queue,轉換成使用者層級的審查,彙整與該使用者相關的所有媒體
  • Move:把 Job 轉移至其他 Queue
  • 已為相關 Item Type 設定的**自訂 Action**,但不包含目前 Queue 隱藏的項目

Policy 會直接顯示在 Job 檢視中,內容審查員無須離開審查流程,就能查閱處置指引。

台灣使用提醒 Enqueue to NCMEC 屬於 Coop 內建的美國兒少安全工作流程。選用前需確認組織資格、資料處理方式、適用法律及台灣通報安排,不應只因介面提供此選項就直接送出資料。

留言

內容審查員可以在任何 Job 加入內部留言,與團隊成員溝通,例如標記疑慮、要求第二意見,或記錄未來可能需要的脈絡。留言只對組織成員可見,不會顯示給遭檢舉的使用者或檢舉者。

審查員身心健康

審查員的身心健康與安全是信任與安全工作的核心關切。Coop 提供可設定的選項,降低審查有害內容造成的影響。

  • Blur:圖片與影片預設模糊。將游標移到圖片上可暫時取消模糊,移開後再次模糊。播放影片時會取消模糊。您可以設定模糊強度或完全停用

  • Grayscale:以灰階顯示媒體,不使用全彩,可降低圖像內容的情緒衝擊

  • Mute Videos:無論裝置音量為何,預設將所有影片靜音

全組織預設值

Coop 的組織身心健康設定,可設定媒體的基本保護選項,包括模糊程度、灰階切換與影片自動靜音。

管理員可以在 Settings → Wellness 設定適用於組織所有使用者的身心健康預設值。

每位使用者都能在 Account → Wellness 以個人偏好覆寫組織預設值。

Signals

Signals 是 Coop 發揮功能的關鍵。您可以用 Signal 分析 Item 並判斷其特徵。Signal 可以只是檢查關鍵字,也可以複雜到將 Item 交給大型語言模型或其他 AI 模型處理。Signal 接收 Item,輸出可用於自動化內容治理決策的資訊。

Coop 提供可在 Rule 中使用的 Signals 函式庫,各 Signal 都能調整嚴格或寬鬆程度。例如,主要服務兒童的平台可能希望阻止任何裸露或性內容,因此可建立 Rule,選用裸露分類器等偵測裸露的 Signals。若裸露分類器 Signal 對使用者個人圖片給出 95% 分數,也就是判斷圖片含裸露內容的可能性為 95%,Rule 就可能自動停權該使用者。

使用 Signal 的流程如下。

  1. 設定整合:管理員加入外部服務的 API 憑證
  2. 在 Rules 中使用 Signal:在內容治理規則中引用 Signal
  3. 內容評估:提交內容時呼叫 Signal 並取得分數
  4. 執行 Action:分數超過門檻時,執行 Rule 所設定的 Action

Signals 函式庫包含文字分析、地點比對與第三方 API 整合。

文字分析

Coop 提供多種分析文字的 Signals。

  1. 精確關鍵字比對:在內容中尋找完全相同的詞語或片語

  2. 正規表示式比對:使用正規表示式在 Item 中尋找文字模式

  3. 文字變體比對:Coop 提供偵測常見文字字串變體的演算法,特別適合找出使用 leetspeak、替換字元、在字詞中加入標點符號或以其他方式規避處置的惡意行為者。例如,尋找 Hello 時,也會將 h3||0helllllllloooo 判定為符合

地點比對

您可以建立針對特定地點的 Rule。要使用地點比對,傳送至 Coop 的每個 Item 都需要包含 geohash,代表建立該 Item 之使用者的經緯度位置。接著建立只對特定地點內或附近 Item 採取 Action 的 Rule。也可建立包含 geohash 位置的 Matching Bank,以便在同一處管理大量地點。

第三方整合

按一下即可連接 Google Content Safety API、OpenAI Moderation API 與 Zentropi CoPE 等安全服務 API。Coop 已內建多種整合,只需輸入 API 金鑰。每項整合都有 model card,以一致且可比較的方式說明運作方式。

詳細資訊見整合文件

自訂整合

部署 Coop 的平台可以透過自訂整合加入任何 Signal,例如自建機器學習模型,或使用 Coop 無法直接存取的內部資料。詳細資訊見自訂整合

台灣使用提醒

  • Signal 分數是模型或規則輸出,不代表事實或法律判定。自動採取刪除、停權等高影響 Action 前,應評估誤判、偏誤與人工複核安排
  • 裸露分類器等模型未必以台灣繁中內容或在地脈絡驗證,翻譯文件不構成效能證明
  • 地點比對與外部 API 可能涉及個人資料及跨境傳輸,應依實際資料流向另行評估

調查

Investigation 工具可讓您使用唯一 ID,在 Coop 查找任何 Item 或使用者,並查看 Coop 所知的全部資訊,不必先進入審查 Queue。

Coop 的 Investigation 工具,使用者資料已遮蔽,畫面顯示 Coop 所知的實體或使用者資訊。

輸入 Item 或使用者的唯一 ID 後,可查看下列資訊。

  • Item 本身,包括 Coop 所保存的全部 Fields 與中繼資料
  • 建立 Item 的使用者及其相關中繼資料,若適用
  • 對 Item 及其建立者採取 Action 的完整歷程,包括每項 Decision 的作成者
  • 相關 Item,例如同一 Thread 中前後相鄰的留言,以提供脈絡

您也可以從 Review Console 的 Job 直接進入 Investigation,方法是在 Job 檢視點選 Item 或使用者。

採取 Action

即使不在審查 Queue 中,也可以直接從 Investigation 對 Item 採取 Action。使用 Take action on this item 表單,選擇 Action,依需要選擇 Policy,再按下 Submit Actions

這適合用來處理一般審查流程以外的內容,例如從其他管道收到 Report 後調查使用者,或在先前 Decision 之後採取後續 Action。

反向處理 Action

Coop 沒有內建復原功能。若要反向處理 Action,例如解除使用者停權,需要先在 Settings 建立呼叫平台反向端點的自訂 Action。例如建立「Unban user」Action,呼叫平台的解除停權 API。

設定完成後,可從 Investigation 執行該 Action,或透過 Recent Decisions 紀錄前往 Item。執行 Action 時,Coop 會將 callback 傳送至平台,由平台完成反向處理。

台灣使用提醒

Investigation 可能集中顯示內容、帳號、中繼資料、處置歷程與相關 Item。應依職務限制存取、記錄查詢與 Action,並避免為了「提供脈絡」而無限制擴張資料範圍。反向處理 Action 也應保留與原始 Decision 的關聯,避免稽核紀錄斷裂。

批次處置

有時您可能需要手動對一個或多個 Item 觸發 Action,不先加入審查 Queue 等待內容審查員處理。例如,同事可能緊急轉交一項需要立即刪除的內容,或執法機關要求您針對犯罪活動停權特定使用者。

只要有 Item 的唯一 ID,就能透過批次處置手動觸發 Action。流程如下。

  1. 貼上要執行 Action 的 Item ID,一次最多 1,000 個
  2. 選擇要對 Item 套用的 Actions
  3. 選擇要與 Actions 關聯的 Policies
  4. 選擇 Execute Bulk Action

完成後,Actions 會立即觸發。

適合使用批次處置的情況

批次處置適用於下列情況。

  • 同事轉交需要立即處置的緊急內容,略過 Queue 可避免等待內容審查員取得 Job 的延誤
  • 執法機關要求中列出應移除或保存的特定使用者或內容 ID
  • 需要一次處理大量 ID,例如清理由已知惡意行為者跨多篇貼文散布的垃圾內容

例行內容治理應優先使用 Queue 審查,讓內容審查員在採取 Action 前查看每個 Item 的脈絡。若需要先查找個別 Item 及其歷程再決定,請使用調查工具。

重要注意事項

  • 沒有個別 Item 脈絡:審查員只會看到貼上的 ID,不會看到 Item 內容或歷程
  • 立即執行,且無法從使用者介面復原:確認後立刻執行 Action,沒有 undo
  • 不執行路由或 Signal 評估:Item 會略過 Rule,直接進入所選 Action

紀錄

批次 Actions 會與 Queue Decision 一起出現在 Recent Decisions 紀錄中,因此可完整稽核處置內容、執行者及所依據的 Policies。

台灣使用提醒

執法機關或其他外部單位提出要求,不會自動產生平台執行批次移除、停權或保存資料的權限與義務。執行前應確認請求真實性、法律依據、核准責任、範圍及保存要求。由於批次處置缺乏個別脈絡且立即生效,建議採雙人確認、先以小批次驗證,並預先準備可追溯的反向 Action。

檢舉

平台使用者檢舉內容時,平台會把該檢舉送至 Coop 的 Report API。Coop 據此建立內容治理 Job,再把 Job 送到適當的審查 Queue,交由內容審查員處理。

Report 是使用者產生的 signals 進入內容治理工作流程的主要方式。Report 會攜帶檢舉者身分、遭檢舉內容、檢舉理由,以及討論串前後訊息或作者近期活動等選填脈絡。

Coop 如何處理檢舉

平台傳送 Report 時,Coop 會進行下列步驟。

  1. 為遭檢舉的 Item 建立內容治理 Job
  2. 評估 Routing Rules,決定 Job 應進入哪個 Queue
  3. 將 Job 送到該 Queue,若沒有符合的 Rule,則送到預設 Queue
  4. 讓該 Queue 中下一位可用的內容審查員取得 Job

reportedForReason.csamtrue,Job 會直接送到 NCMEC Queue,不經一般 Routing Rule 評估。詳細資訊見兒少安全(NCMEC)

將檢舉傳送至 Coop

Report 透過 POST /api/v1/report 提交。完整 API schema,包括欄位定義、型別與必要條件,見 Report API

將惡意檢舉者的檢舉設為無效

若同一位平台使用者大量標記未違規內容並阻塞審查 Queue,具有 EDIT_MRT_QUEUES 權限的內容審查員,可在 Manual Review Tool 的任何 Report 詳細資料檢視中,使用「Invalidate reports」Action,將該檢舉者所有待處理 Report 設為無效。

預設情況下,Action 只影響目前 Job,會從 Job 的檢舉歷程移除該檢舉者的紀錄。若移除後沒有其他檢舉者或 Report 來源,例如自動偵測器,Job 就會從 Queue 移除。若要處理組織內所有待處理 Job,請在確認視窗勾選「Apply across the whole organization」。已決定或已關閉的 Job 不會修改。

這是單次操作,不是持續性的封鎖清單。相同檢舉者日後提出的 Report 仍會正常進入系統,若行為持續,需要再次設為無效。反覆惡意檢舉的使用者,應由平台層級停權或限制發言。

每次設為無效的操作都會在伺服器產生 trace span,記錄執行操作的內容審查員、目標檢舉者、選填理由與結果數量。目前,該 span 是臨時稽核查詢的單一真實來源。

申訴

使用者要對內容治理決策提出異議時,應使用 Appeals API。這是與 Report 分開的流程。詳細資訊見申訴

台灣使用提醒

檢舉者身分、檢舉內容與作者活動都可能包含個人資料或敏感資訊。設定 Report payload、內部權限、保存期限與稽核存取前,應先採資料最小化原則。reportedForReason.csam 的自動路由也不等同完成台灣法定通報。

申訴

平台使用者不滿意內容治理決策並要求重新審查時,平台可以透過 Appeal API 將申訴送至 Coop。Coop 會把它送到審查 Queue,讓內容審查員檢查原始決策,並選擇維持或推翻。

申訴如何顯示在 Coop

若要讓申訴出現在審查 Queue,請將遭申訴的決策傳送至 Coop,並建立以申訴為目標的 Routing Rule。

Coop Routing Rule 設定為將所有申訴送至專用申訴 Queue。

申訴會以 Job 形式進入 Review Console,與 Report 類似。Job 會顯示原本遭處置的 Item、引用的 Policy、使用者申訴理由,以及提交申訴時加入的其他脈絡。

維持或推翻申訴

內容審查員可以查看原始內容治理決策,並選擇下列結果。

  • Uphold:原始 Action 正確,駁回申訴
  • Overturn:原始 Action 不正確,接受申訴

作成決策後,Coop 會透過 Appeal Decision Callback 將結果送回平台,讓平台把結果告知使用者。

實作

實作 Appeal API 前,請先完成基本概念所述設定。您需要先在 Coop 設定 Item TypesActionsPolicies

完整 request schema 與驗證要求見 Appeal API,Appeal Decision Callback 則見處理 Actions

台灣使用提醒

申訴流程應與平台上的通知、理由說明、處理期限、權限及結果回覆方式一起設計。Coop 可協助路由與記錄,但平台仍需自行定義誰能複核、哪些資料可見,以及如何處理原決策已造成的影響。

兒少安全(NCMEC)

重要適用提醒 本頁翻譯 Coop 與美國 National Center for Missing & Exploited Children(NCMEC)CyberTipline 的產品工作流程。內容不構成台灣法律意見,也不代表台灣平台可直接依相同步驟履行通報義務。啟用前需要由具資格的法律與兒少安全專業人員,確認組織資格、證據與資料處理、通報對象、時限、保存、跨境傳輸及審查員保護。

Coop 支援透過 CyberTipline Reporting API,向美國 National Center for Missing & Exploited Children(NCMEC)通報兒少性虐待素材(CSAM)。Coop 處理完整的偵測、路由與通報生命週期,包括自動標記已知或疑似 CSAM、送至專用 NCMEC 審查 Queue,並引導審查員完成 CyberTip 提交。

設定方式見 NCMEC 整合文件

存取權與角色

由於 NCMEC 資料高度敏感,Coop 只允許 Admin、Moderator Manager 與 Child Safety Moderator 角色存取。

內容如何進入 NCMEC Queue

內容可透過四種方式進入 NCMEC 審查 Queue。

  1. 雜湊比對(HMA):Coop 透過 Hasher-Matcher-Actioner(HMA)整合,將上傳媒體與 NCMEC 的已知 CSAM 雜湊資料庫比對。雜湊符合是強而可靠的 Signal

  2. 新出現的 CSAM 偵測(Content Safety API):對於沒有已知雜湊的內容,Coop 整合 Google Content Safety API,分類圖片是否可能為 CSAM。高信心結果可直接送至 NCMEC Queue,也可先送至初步分類 Queue

  3. 標記為 CSAM 的新進 Report:平台傳送標記為 CSAM 的使用者 Report 至 Coop 時,Coop 會直接送至 NCMEC Queue,不評估一般 Routing Rules

  4. 人工升級:在任何審查 Job 中,具有 NCMEC 存取權的內容審查員都可從 Action 清單選擇 Enqueue to NCMEC,立即將 Job 移至 NCMEC Queue

設定方式見將內容路由至 NCMEC

內容進入 Queue 後的處理

內容透過上述任一路徑進入 NCMEC Queue 後,Coop 會進行下列步驟。

  1. 透過內容 Item 的 creatorId 欄位辨識相關使用者。若 Item 本身是 User Type,則直接使用該 Item
  2. 檢查該使用者是否已有開啟中的 NCMEC Job。若有,加入新內容並更新,不建立重複 Job
  3. 取得平台上曾與該使用者關聯的所有媒體
  4. 建立單一彙整 NCMEC 審查 Job,包含使用者及其所有媒體
  5. 將 Job 送至已設定的 NCMEC Queue

以使用者為中心的彙整方式,會讓同一位使用者即使上傳多項 CSAM,也只建立一個 NCMEC 審查 Job,並向 NCMEC 提交一份較具行動價值的 CyberTip,不會為每項內容分別提交。

資料範圍提醒 「取得所有媒體」可能大幅擴張審查與跨境傳輸的資料範圍。實際採用時應確認是否具有適當權限與必要性、哪些媒體可納入、失敗或誤標時如何停止,以及未送出資料如何隔離與刪除。

審查 NCMEC Job

NCMEC Job 使用者介面與標準審查 Job 不同,設計重點是使用者及與其相關的全部媒體。

NCMEC Reporting Job 檢視,顯示使用者的彙整媒體、industry classification 鍵盤快速鍵、incident type 下拉選單,以及每項媒體的標籤選擇器。

Incident Type

從 NCMEC CyberTipline 定義的類別中選擇適用 incident type。下列英文名稱應依 API 類別原樣使用。

  • Child Pornography,持有、製作與散布
  • Child Sex Trafficking,兒少性販運
  • Child Sex Tourism,兒少性觀光
  • Child Sexual Molestation,兒少性侵害
  • Misleading Domain Name,誤導性網域名稱
  • Misleading Words or Digital Images on the Internet,網路上的誤導性文字或數位影像
  • Online Enticement of Children for Sexual Acts,在線上引誘兒少從事性行為
  • Unsolicited Obscene Material Sent to a Child,未經要求傳送給兒少的猥褻素材

Industry Classification

對每項通報媒體套用由 ESP 指定的 industry classification

分類說明
A1青春期前兒少,露骨性行為
A2青春期前兒少,非露骨裸露或性姿勢
B1青春期兒少,露骨性行為
B2青春期兒少,非露骨裸露或性姿勢

審查介面提供鍵盤快速鍵,以加快分類速度。

File Annotations

對個別媒體套用一個或多個標籤,向 NCMEC 提供更多脈絡。欄位名稱屬於資料交換值,不應翻譯或修改。

標籤說明
animeDrawingVirtualHentai檔案呈現動漫、卡通、虛擬或 hentai 內容
potentialMeme檔案看似因模仿或其他表面上非惡意意圖而分享
viral檔案正在使用者之間快速流傳
possibleSelfProduction檔案可能是自行製作
physicalHarm檔案呈現蓄意造成身體傷害或創傷的行為
violenceGore檔案呈現寫實暴力或殘酷內容
bestiality檔案涉及動物
liveStreaming內容上傳時正在直播
infant檔案呈現嬰兒
generativeAi檔案可能由 AI 生成

提交 CyberTip

完成必要數量的媒體審查並選擇 incident type 後,選擇 Submit to NCMEC。Coop 會自動建立並提交 CyberTip,包括取得補充中繼資料、上傳媒體檔案,以及向 NCMEC 完成報告。技術細節見 CyberTip 提交流程

傳送前必須審查的媒體數量,由組織設定 Media review requirement 控制,位置在 Settings → NCMEC Settings。

  • Review all media,預設值:傳送前必須對帳號上的每項媒體作成 Decision,這是原本的行為
  • Require a minimum number of reviewed media:只需分類已設定的最低項目數,可避免為了通報相關項目而審查數百項媒體

兩種情況都至少要有一項媒體被指定通報類別,不能全部是 None,才能傳送 Report。

查看已提交 Report

CyberTip 提交後,Report 會保存在 Coop,並可從 NCMEC Reports 資訊儀表板存取。Report 紀錄包含下列資訊。

  • NCMEC 指派的 Report ID
  • 遭通報的使用者
  • Report 包含的所有媒體
  • 完整 CyberTip XML
  • 已上傳的所有補充檔案
  • 已上傳的所有對話 Thread CSV
  • 提交的是測試 Report 或正式 Report

審查與治理要求

  • 本頁目前狀態為 translated,尚未完成法律與兒少安全領域審查
  • 真實 CSAM 不應用於一般翻譯、介面測試、教學截圖或開發環境驗證
  • 應為內容審查員提供專門訓練、最小化暴露、身心健康保護、事件升級與事後支持
  • 測試與正式環境必須明確分離,並防止測試操作誤送正式通報
  • API 傳送成功不等同於已完成所有台灣通報、證據保存、使用者處置或受害者保護責任

指標與報告

Coop 在兩個介面追蹤內容治理活動。Overview 資訊儀表板提供營運指標,Recent Decisions 紀錄則提供完整稽核軌跡。

Overview

Coop Overview 顯示主要營運指標,包括已採取的 Action 總數、待審查 Job、自動與人工 Action 百分比,以及最常見的 Policy 違規。

Overview 資訊儀表板提供內容治理活動的高階概況。所有指標都可在可設定期間內,依小時或每日篩選。Overview 顯示下列內容。

  • 已採取的 Action 總數:所選期間內所有內容治理 Decision 的數量
  • 待審查 Job:目前在 Queue 中等待內容審查員處理的 Job 數量
  • 自動與人工 Action:由 Proactive Rules 與人工審查員作成之 Decision 的百分比分布
  • 最常見的 Policy 違規:哪些 Policy 產生最多 Action
  • 每位內容審查員的 Decision:工作在審查團隊中的分布情形
  • 每條 Rule 的 Action:哪些 Rule 最常觸發,只有啟用 Rule 時才顯示
  • 各 Policy 的違規:一段時間內,各 Policy 之下採取的 Action 數量

Recent Decisions

Coop Recent Decisions 頁面,顯示 Coop 內所有 Action 與執行者的紀錄。畫面提供重新整理表格、下載所有 Decision,以及只下載使用者略過 Job 的按鈕。

前往 Review ConsoleRecent Decisions,即可查看 Coop 中採取的每項 Action,包括誰在何時對何種內容作成 Decision。您可以從任何紀錄前往完整 Job,進一步調查或採取其他 Action。

篩選 Recent Decisions。

可下載完整紀錄,也可依 Decision、Policy、Queue、內容審查員與日期範圍篩選後下載。特別適合下列用途。

  • 透明度報告:匯出 Decision,納入提供給主管機關或監督單位的報告
  • 品質保證與稽核:抽樣個別內容審查員或自動 Rule 作成的 Decision,檢查一致性與準確度
  • 推翻工作流程:從紀錄的 Decision 回到原始 Job,並依需要執行反向 Action

也可另外下載只包含內容審查員曾經 skip 的 Job。這有助於找出可能長期難以判定的內容。

台灣使用提醒

  • Action 數量與處理速度無法單獨代表治理品質。建議同時觀察誤判、推翻率、申訴結果、等待時間、審查員負荷與不同 Policy 的差異
  • Decisions per moderator 適合分析工作分配,不宜脫離案件難度與身心健康因素,直接當成個人績效排名
  • 匯出資料可能含帳號、內容、理由與審查員資訊。提供給外部單位前應確認目的、欄位最小化、去識別、存取權與保存期限

管理與設定

管理員從 Settings 選單管理組織設定,以及 ItemActionPolicy使用者存取與整合的個別設定。

Settings

Settings 設定組織資料與全組織行為,包括單一登入、申訴、Review Console、審查員身心健康等功能。許多可切換功能預設關閉,請依平台與團隊需要選擇啟用。

Organization

Coop 組織的識別與聯絡資訊。

Settings 資訊儀表板的 Organization 分頁,包含組織名稱、電子郵件、網站網址與值班警示電子郵件欄位。

On-Call Alert Email 需要在 Coop 部署中整合電子郵件服務。Coop 支援電子郵件服務整合,但不會預先設定服務。

Single Sign-on

啟用以 SAML 為基礎的 SSO,讓使用者透過組織的身分提供者驗證,不使用電子郵件與密碼。Coop 支援任何 SAML 2.0 身分提供者。以 Okta 設定的範例,見部署指南的 Single Sign-on

Settings 的 SSO 分頁,顯示 SAML/SSO 啟用切換,以及 SSO URL 與 SAML Certificate 欄位。

Appeals

啟用使用者對 Coop Decision 提出申訴,並設定平台的申訴 callback URL、headers 與 body。

Settings 的 Appeals 分頁,顯示啟用申訴的切換,以及 callback URL、headers 與 body 欄位。

Review Console

設定組織內內容審查員使用 Review Console 的方式,包括內容審查員必要條件、Queue 管理行為與 webhooks。

Settings 的 Review Console 分頁,包含 Require Policy、Require Decision Reason、Hide Skip Button、Enable Preview Jobs View 切換,以及 Ignore Callback URL 欄位。

Wellness

設定 Review Console 顯示媒體時的審查員身心健康保護,包括模糊、灰階與靜音,以減少審查有害媒體時的暴露。

Settings 的 Wellness 分頁,包含 Blur Media 滑桿、Greyscale 切換與 Mute Videos 切換。

其他

不適合歸入其他分頁的設定,包括 Partial Items 端點,以及讓 Job Decision 引用多項 Policy 的功能。

Settings 的 Other 分頁,包含 Partial Items Endpoint、Partial Items Request Headers、Reporting Rules、Multiple Policies Per Action 與 User Strike TTL。

Item Types

Item Type 代表平台上的不同實體類型。詳情見基本概念的 Item Type

Item Type 設定範例。從 firehose 取得貼文,Schema 包含傳送至 Coop 的文字、唯一 ID 與欄位格式。

建立 Item Type 時,請定義 Schema,指定要包含及顯示給內容審查員的 Fields。這些 Fields 也可用於 Rule 邏輯,連接 Signals 以進行路由或自動化。

Actions

Action 代表 Proactive Rule 或內容審查員 Decision 可對 Item 執行的任何動作。詳情見基本概念的 Actions

已設定的自訂 Action 表格,包括傳送警告、標記為垃圾內容、刪除內容、刪除帳號與封鎖電子郵件。

Action 會與平台上的 API 端點配對。技術細節見處理 Actions

在 Coop 建立 Action,設定名稱、說明、可執行 Action 的 Item Type,以及 callback URL。

Policies

Policy 是平台用來治理使用者行為的一組規則與指引。詳情見基本概念的 Policy

Policy 資訊儀表板顯示 Fraud、Nudity、Scams 與 Spam 四項 Policy,並提供建立 Policy、加入子 Policy、編輯與刪除選項。

從 Coop 使用者介面加入的 Policy,會直接顯示在 Review Console 的 Job 檢視,供內容審查員查閱。

使用者管理

Coop 使用角色式存取控制,確保只有適當的人員能存取相應資料。

使用者管理頁面,顯示不同使用者的電子郵件、角色、核准狀態與建立日期。

您可以從 SettingsUsers 邀請使用者,複製邀請連結直接分享,或設定電子郵件服務自動寄送。

使用者邀請流程。

Roles

Coop 內建七種預設角色,可直接對應多數團隊結構。

  • Admin:管理整個組織,完整控制 Coop 內所有資源與設定
  • Rules Manager:可建立、編輯及部署 Live Rules,執行 retroaction 與 backtests、查看 Rule insights、管理 Policies,以及使用 Investigation 工具和批次處置。無法管理使用者、Queues 或其他組織層級設定
  • Moderator Manager:可查看及編輯 Review Console 內所有 Queues、管理內容審查員權限,以及使用 Investigation 工具和批次處置,也能查看兒少安全資料
  • Child Safety Moderator:具有與 Moderator 相同的權限,另可審查 Child Safety Jobs 與查看先前 Child Safety Decisions
  • Moderator:可存取 Review Console,但只能審查獲准查看之 Queue 中的 Job,無法查看任何兒少安全相關 Job 或 Decision
  • Analyst:可修改及測試 Draft 和 Background Rules、執行 backtests,以及查看 Rule insights 與 Investigation 工具。無法建立或編輯 Live Rules、執行 Retroaction,或存取 Review Console
  • External Moderator:只能審查 Review Console 中的 Job,無法查看任何 Decision 或使用其他工具

Admin 可從 SettingsUsersRoles 自訂任何角色。開啟角色卡片上的選單,即可編輯顯示名稱、說明與權限。個別權限依領域分組,包括組織管理、Rules、人工審查與 Investigation,可個別開啟或關閉。View Permissions 則會開啟矩陣,一次比較所有角色。

Roles Management 分頁顯示七種預設角色的卡片,以及各角色名稱、說明、使用者數與權限數。

變更會立即套用至所有具有該角色的使用者。只有具有 Manage Roles 權限者能存取角色編輯器,Admin 預設具有此權限。

API Keys

Coop 使用 API keys 驗證平台與 Coop 之間的 request。

Coop API key

平台傳送 request 至 Coop 時,應在每次 request 中以 HTTP header 加入組織 API key。您可在 SettingsAPI Keys 查找或輪替金鑰。

X-API-KEY: <<apiKey>>
Content-Type: application/json

若要確認 Action 端點收到的 request 確實由 Coop 傳送,請使用 SettingsAPI Keys 顯示的 webhook signature verification key。實作細節見開發指南的 API Keys 與 Authentication

台灣使用提醒

  • 角色與權限變更會立即生效。特別是兒少安全資料、批次處置、Live Rules、API keys 與角色管理,應遵循最低權限並定期複核
  • 邀請連結、SAML certificate、API key 與 webhook verification key 都應視為安全敏感資訊,避免放入 issue、聊天紀錄、螢幕截圖或版本控制
  • Callback URL、headers 與 body 可能包含 secrets 或個人資料,應使用加密傳輸、限制目的地並建立金鑰輪替及事件應變流程
  • 「External Moderator」權限較少,但仍可能接觸遭檢舉內容。外部人員的契約、保密、訓練、身心健康與帳號撤銷程序仍需另行安排

開始使用

本頁協助第一次進行 Coop 本機開發時快速完成設定。若要測試與展示,可改用 Docker Images

Note

建議先熟悉 Coop 的基本概念,取得更多脈絡。

本指南假設讀者了解基本命令列操作,例如從 Terminal 使用 bashzsh。Prerequisites、詳細設定、疑難排解等資訊,見本機開發。系統元件與資料流見架構

執行 Coop 的步驟如下。

  1. 若尚未完成,使用 git clone repository 並進入 coop 資料夾

    git clone https://github.com/roostorg/coop.git && cd coop
    
  2. 確認已安裝 prerequisites,包括 nvmdocker 與正確版本的 Node.js

    # coop/
    nvm --version && nvm install && nvm use
    docker --version
    

    應看到類似下列 output。

    0.40.4
    Found '.nvmrc' with version <24.18.0>
    v24.18.0 is already installed.
    Now using node v24.18.0 (npm v11.11.0)
    Docker version 29.4.3, build 055a478
    

    若出現 error,請參閱本機開發的 Prerequisites。版本範例只代表此文件來源當時狀態,實際版本應以 repository 的 .nvmrc 與目前文件為準。

  3. 從 root folder 與各 sub-package 使用 npm 安裝 dependencies

    # coop/
    npm install
    (cd db && npm install)
    (cd server && npm install)
    (cd client && npm install)
    
  4. db/server/client/ 複製範例 environment files。預設值可供本機開發與展示使用。詳情見本機開發的 Environment 設定

    # coop/
    cp db/.env.example db/.env
    cp server/.env.example server/.env
    cp client/.env.example client/.env
    
  5. 啟動所有 backing services,包括 databases 與 Queues。Ports 與詳細資訊見本機開發的 Docker services

    # coop/
    npm run up
    

    等待 PostgreSQL、ClickHouse、ScyllaDB 與 Redis 進入 healthy 狀態後再繼續。可使用 docker ps 查看進度。

  6. 建立 databases 並執行 migrations,確認設定正確

    # coop/
    npm run db:create -- --env staging --db api-server-pg
    npm run db:create -- --env staging --db scylla
    npm run db:create -- --env staging --db clickhouse
    
    npm run db:update -- --env staging --db api-server-pg
    npm run db:update -- --env staging --db scylla
    npm run db:update -- --env staging --db clickhouse
    
  7. 使用 server/ folder 中的 script 複製 static asset files

    # coop/
    cd server
    
    # coop/server/
    npm run copy-assets
    
  8. server/ folder 使用 create-org script,提供適當資訊,建立 organization 與 admin user

    範例如下。

    # coop/server/
    npm run create-org -- \
      --name "Your Organization" \
      --website "https://example.com" \
      --email "email@example.com" \
      --firstName "Jane" \
      --lastName "Doe" \
      --password "correct-horse-battery-staple"
    

    Script 會輸出 org ID 與初始 API key,請立即複製並保存在安全位置。

  9. 最後啟動 application。其他選項,包括為除錯分別啟動不同 components,見執行 application

    若仍在 server/,先回到 project root,再啟動 server 與 client。

    # coop/server
    cd ..
    
    # coop/
    npm run start
    

    使用執行 create-org 時提供的 credentials,從 localhost:3000登入。第一次載入可能需要一點時間。

本機安全提醒

  • .env、初始 API key、admin password 與 signing keys 不得 commit、貼入 issue 或公開終端輸出
  • 範例中的 staging、localhost 與預設 credentials 只適合隔離的本機環境,不應直接搬到正式環境
  • 執行 migration 前應確認目標 database 與 environment,尤其避免將 staging seed data 寫入 production
  • 本機媒體與兒少安全整合測試只能使用安全合成資料,不得使用真實 CSAM、私密影像或可識別個案

本機開發

本頁提供本機開發的詳細設定與疑難排解資訊。

Note

建議先熟悉 Coop 的基本概念,取得更多脈絡。

本頁著重提供詳細資訊與參考資料。若只想快速開始執行,請閱讀開始使用。系統元件與資料流見架構

Prerequisites

  • Operating System:macOS、Linux,或使用 WSL2 的 Windows
  • git:clone repository 與參與貢獻時使用
  • Node.js 24nvmnpm
  • DockerDocker Compose
  • Bare instance 最少需要 4 GiB RAM,開發環境建議使用 8 GiB 以上

確認使用 repository 建議的 Node.js 版本。

nvm install && nvm use

Dependencies

Coop repository 包含多個 components,各自以 npm package 管理及安裝 dependencies。

npm install
(cd db && npm install)
(cd server && npm install)
(cd client && npm install)
(cd migrator && npm install)

Environment 設定

db/server/client/ 中的 .env.example 複製為 .env,再依環境調整。預設值可供本機開發及展示使用。範例檔列出所有可用選項及說明。

db/.env

Postgres、ClickHouse 與 Scylla database connection settings。

server/.env

Redis connection settings、integrations 使用的 external API keys、session secrets 與 JWT signing keys。

client/.env

Vite、content proxying 與產生 sourcemaps 的設定。

Warning

.env 可能包含 secrets,不得 commit、貼入 issue 或放入公開 log。範例 secrets 只適用於隔離的本機環境。

Docker services

npm run up 會使用 Docker 啟動 backing services。

ServicePort說明
PostgreSQL5432Primary database
ClickHouse8123、9000Analytics warehouse
ScyllaDB9042Item submission history
Redis6379Caching 與 job queues
Jaeger16686Tracing UI
OTEL Collector4317Telemetry collection

檢查 service health。

docker ps
docker logs <container-name>

停止 services。

npm run down

Database 操作

建立 databases

npm run db:create -- --env staging --db api-server-pg
npm run db:create -- --env staging --db scylla
npm run db:create -- --env staging --db clickhouse

執行 migrations

npm run db:update -- --env staging --db api-server-pg
npm run db:update -- --env staging --db scylla
npm run db:update -- --env staging --db clickhouse

其他 commands

npm run db:add -- --name <migration-name> --db api-server-pg
npm run db:clean    # Drop and recreate (destructive)
npm run db:create   # Create database
npm run db:drop     # Drop database

Caution

db:cleandb:drop 會刪除資料。執行前請核對 environment、database target 與備份狀態,不要將本機範例 command 直接用於 production。

Migration locations

db/src/scripts/
├── api-server-pg/    # PostgreSQL
├── clickhouse/       # ClickHouse
└── scylla/           # ScyllaDB

執行 application

為了方便使用,repository root 的 start npm script 會啟動 client、server 與 GraphQL codegen,並開啟 web browser。compile script 會執行相同工作,但不開啟 browser。

npm run start

或使用下列 command。

npm run compile

個別 services

若要分別啟動 services 以協助除錯,請在不同 terminal windows 或 tabs 中,分別執行 serverclient packages 的 start npm script。

在第一個 terminal 啟動 server。

cd server && npm run start

在第二個 terminal 啟動 client。

cd client && npm run start

若要持續更新 GraphQL schema changes,可選擇在第三個 terminal 執行下列 command。

npm run generate:watch

Background workers

Item submissions 由 BullMQ worker 從 Redis Queue 取出後非同步處理。若要在本機處理 Items,請在另一個 terminal 執行 worker。

cd server
npm run runWorkerOrJob ItemProcessingWorker

若沒有執行此 worker,提交的 Items 會進入 Redis Queue,但不會被處理。其他可用的 workers 與 jobs 列於 server/iocContainer/services/workersAndJobs.ts

使用 distributed tracing

cd server && npm run start:trace

可在 localhost:16686 查看 traces。

存取位置

ServiceURL
Clienthttp://localhost:3000
API Serverhttp://localhost:8080
GraphQLhttp://localhost:8080/graphql
Jaeger UIhttp://localhost:16686

Testing

# Server
cd server
npm run test              # Watch mode
npm run test:prepush      # Single run
npm run test:integ        # Integration tests

# Client
cd client
npm run test              # Watch mode
npm run test:prepush      # Single run

# Full validation (run before pushing)
npm run check:prepush

在本機執行 CI

所有 PR checks 都定義為 docker compose services,可在本機重現 CI jobs。

CI job本機 command
check_generated_graphqldocker compose run --rm codegen-check
check_api_server(lint)docker compose run --rm backend npm run lint
check_api_server(build)docker compose run --rm backend npm run build
run_frontend_checks_if_changed(lint)docker compose run --rm client npm run lint
run_frontend_checks_if_changed(build)docker compose run --rm client npm run build
check_api_server(test)docker compose run --rm test

執行完整 suite,遇到第一個 failure 時停止。

docker compose run --rm codegen-check \
  && docker compose run --rm backend npm run lint \
  && docker compose run --rm backend npm run build \
  && docker compose run --rm client npm run lint \
  && docker compose run --rm client npm run build \
  && docker compose run --rm test

關閉 services。

docker compose down        # stop containers, keep DB volumes
docker compose down -v     # also drop DB volumes (fresh DBs next run)

Caution

docker compose down -v 會刪除目前 Compose project 管理的 database volumes。執行前請確認工作目錄、Compose file 與資料是否已有備份或可重建。

check_migration_order 只在 GitHub Actions 執行。這是 GitHub-specific check,本機不需要執行。新增 migration 時,請以 date -u +"%Y.%m.%dT%H.%M.%S" 產生 filename prefix,讓 CI check 通過。

GraphQL 開發

Coop 使用 schema-first GraphQL 與雙向 code generation。

npm run generate          # One-time
npm run generate:watch    # Watch mode

產生的 files 如下。

  • client/src/graphql/generated.ts
  • server/graphql/generated.ts

Schema changes 會觸發 client 與 server 重新編譯。若發生 regeneration loop,請停止 watch mode,再手動執行。

Backend GraphQL definitions 在每個 block 開頭以 /* GraphQL */ 標示,大多位於 /server/graphql/。Frontend GraphQL 定義在使用它的 components 旁,因此 file 可能使用並未定義於同一檔案內的 queries。

Management scripts

server/bin/ 中的兩個 utility scripts 可協助執行常見操作。

  • npm run create-org:建立新 organization、admin user 與 API key
  • npm run get-invite:取得已透過 UI 邀請之使用者的 signup link

詳細用法與範例見 server/bin/README.md

HMA 開發

執行 npm run up 時,HMA 會與其他 backing services 一起自動啟動。

server/.env 已預先以 HMA_SERVICE_URL=http://localhost:9876 設定 HMA。本機開發不需要額外 environment 設定。

Image URL 存取

向 Coop 提交 Items 時,image URLs 必須能由 HMA Docker container 存取,只有 browser 或 Node.js server 能存取仍不足以完成處理。

HMA 會自行取得 image 並計算 hash。因此,localhost URLs 會在沒有明顯 error 的情況下失敗,HMA 將回傳空 hashes、image similarity Signal 不會進行評估,也不會觸發 Rule。

本機開發時,若從 host machine 提供 images,請在 /etc/hosts 加入下列內容。

127.0.0.1 host.docker.internal

提交 Items 時,請在 image URLs 使用 host.docker.internal:<port>。此 URL 可同時由 browser 與 Docker container 正確解析。

Note

修改 /etc/hosts 通常需要系統管理權限。請先確認既有內容,僅新增所需 mapping,不要覆寫整份檔案。

疑難排解

ScyllaDB 尚未就緒

ScyllaDB 需要 30 至 60 秒完成初始化。若在 npm run up 後立即執行 migrations 而失敗,請等待後再重試。

ClickHouse migration 失敗

確認 .env 已設定 CLICKHOUSE_USERNAMECLICKHOUSE_PASSWORD

Port 衝突

lsof -i :3000    # Client
lsof -i :8080    # Server
lsof -i :5432    # PostgreSQL

重設所有本機資料

npm run down
docker volume prune    # Warning: removes all Docker volumes
npm run up
npm run db:update -- --env staging --db api-server-pg
npm run db:update -- --env staging --db clickhouse
npm run create-org

Caution

docker volume prune 會刪除整個 Docker environment 中所有未被 container 使用的 volumes,不只 Coop 的 volumes,且可能影響其他 projects。執行前請先列出並核對目標;若只需重設 Coop,優先使用限定於正確 Compose project 的操作。

直接連線至 databases

# PostgreSQL
psql -h localhost -U postgres -d postgres
# Password: postgres123

# ClickHouse
clickhouse-client --host localhost --user default --password clickhouse

# Redis
redis-cli

上述 credentials 為本機範例值,不應用於共享或 production 環境。

Code quality

npm run lint           # ESLint
npm run prettier       # Prettier (check only; use `npm run prettier:fix` to write, alias `npm run format`)
npm run check:prepush    # Run before pushing

架構

本頁提供 Coop system architecture 的開發與維運概覽。

概覽

Coop 採 monorepo 架構,包含 React frontend、Node.js backend 與 multi-database architecture,設計目標是支援大規模、高吞吐量的內容治理。Coop 具備下列能力。

  • Operations 與 policy teams 可管理檢舉送往哪個 Queue、每項處置需要累積多少次違規等設定,不需由 engineers 修改 backend code
  • 同時支援自動處理與人工審查流程
  • 提供具 role-based access control permissions 的 UI
  • 內建 image 與 video media player
  • 內建符合實務建議的審查員身心健康功能
  • 使用 webhook-based architecture 將事件與其後續效果連接
  • 記錄 Actions 的 audit trail、Action metadata,包括發生時間及執行者,以及對應 Policy
  • 支援 dev/staging environments,供人工測試與 automated integration tests 使用

Technology stack

LayerTechnologies
FrontendReact、TypeScript、Ant Design、TailwindCSS、Apollo Client
BackendNode.js、Express、Apollo Server、TypeScript
DatabasesPostgreSQL、Scylla(5.2)、ClickHouse、Redis
MessagingBullMQ(Redis)
ORMSequelize、Kysely
AuthPassport.js、express-session、SAML(SSO)
ObservabilityOpenTelemetry

Directory structure

coop/
├── client/                    # React frontend
│   └── src/
│       ├── webpages/         # Page components
│       ├── graphql/          # GraphQL queries/mutations
│       ├── components/       # Shared UI components
│       └── utils/            # Utility Functions
│
├── server/                    # Node.js backend
│   ├── bin/                  # CLI scripts
│   ├── graphql/              # GraphQL schema and resolvers
│   ├── iocContainer/         # Dependency injection setup
│   ├── models/               # Sequelize ORM models
│   ├── routes/               # REST API routes
│   ├── rule_engine/          # Rule evaluation logic
│   ├── services/             # Business logic services including NCMEC
│   └── workers_jobs/         # Background processing
│
├── db/                        # Database migrations
│   └── src/scripts/
│       ├── api-server-pg/     # PostgreSQL
│       ├── clickhouse/        # ClickHouse
│       └── scylla/            # Scylla
│
└── docs/                      # Documentation

Backend service registration

Coop backend 使用 BottleJS 進行 dependency injection,支援 lazy loading、middleware hooks 與 decorators。新 services 需在 server/iocContainer/index.ts 註冊。若要加入新 service 並讓 application 其他部分使用,應從此處開始。

API

Coop 透過 REST APIs 接收內容。所有 API requests 都必須在 x-api-key header 傳入 organization API key。

所有 endpoints 與 request/response schemas 詳見 API 參考

傳入 Coop

平台透過 Items API 將內容傳入 Coop,以進行自動處置。使用者檢舉則透過 Report API 傳入,再路由至 Review Console。

若要回填歷史資料、在 Review Console 取得尚未送至 Coop 的 related Items,並確保查看時 Items 內容為最新狀態,平台可使用 Partial Items API

Coop 傳出的 Actions

Proactive Rule 或內容審查員 Decision 觸發 Action 時,Coop 會向 organization platform 傳送 webhook。Webhook 格式與處理方式詳見處理 Actions

Rules

Coop 支援兩組 Rules,各自使用不同 code paths、storage tables 與 UI surfaces。

Proactive Rules

提交 Item 時,Coop 會取得所有與該 Item Type 關聯的 Proactive Rules。Proactive Rules 會平行執行以決定 automatic Actions,也可能將 Item 傳至 Review Console。

每項 Rule 會以 recursive processing 評估其 conditionSet,從 Item 取出 values,視需要將 values 傳入 Signals,再以設定的 comparators 比較結果。

Rule status 包括 LIVEDRAFTBACKGROUNDEXPIRED

  • Code:/server/models/rules/RuleModel.ts
  • Storage tables
    • manual_review_tool.routing_rules
    • manual_review_tool.routing_rules_to_item_types
    • manual_review_tool.routing_rules_history
    • manual_review_tool.appeal_routing_rules
    • manual_review_tool.appeal_routing_rules_to_item_types
  • UI:/client/src/webpages/dashboard/rules/

Routing Rules

提交檢舉,或 Proactive Rule 將 Item 傳至 Review Console 時,系統會以 Routing Rules 進行評估。第一個成功的 Routing Rule 會將 Item 以 Job 形式路由至適當 Queue,等待審查。

  • Code:/server/services/manualReviewToolService/modules/JobRouting.ts
  • Storage tables
    • public.rules
    • public.rules_and_actions
    • public.rules_and_item_types
    • public.rules_and_policies
    • public.rules_history
  • UI:/client/src/webpages/dashboard/mrt/queue_routing/

Review Console

Review Console 在 codebase 中有時稱為「manual review tool」或「MRT」,是一套以 BullMQ Queue 為基礎的人工審查系統。Items 會因 Rule Actions 或使用者檢舉,以 Job 形式進入 Review Console。每個 Job 會加入檢舉次數、使用者違規次數、related Items 等脈絡,再依 UI 設定的 Routing Rules 路由至具名 Queue。內容審查員以 exclusive lock 領取 Job,確保同一 Job 只有一人處理,再透過執行 Actions 作成 Decision,觸發 downstream callbacks 或 NCMEC 等通報流程。

Queue operations

File/server/services/manualReviewToolService/modules/QueueOperations.ts

Jobs 可由下列來源進入 Queue。

  • Rules engine execution
  • 使用者檢舉
  • Post-action workflows
  • Review Console internal jobs

使用者操作

  • 使用 exclusive locks 取出 Jobs
  • 提交 Decisions
  • 觸發 post-decision webhooks 或 NCMEC 通報

支援的 Decision types

  • IGNORE
  • CUSTOM_ACTION
  • SUBMIT_NCMEC_REPORT
  • ACCEPT_APPEAL
  • REJECT_APPEAL
  • TRANSFORM_JOB_AND_RECREATE_IN_QUEUE
  • AUTOMATIC_CLOSE

Manual Enqueue

{
  orgId: string;
  correlationId: RuleExecutionCorrelationId | ActionExecutionCorrelationId;
  createdAt: Date;
  enqueueSource: 'REPORT' | 'RULE_EXECUTION' | 'POST_ACTIONS' | 'MRT_JOB';
  enqueueSourceInfo: ReportEnqueueSourceInfo | RuleExecutionEnqueueSourceInfo | ...;
  payload: ManualReviewJobPayloadInput;
  policyIds: string[];
}

從 Rules Engine 進入ActionPublisher.ts

case ActionType.ENQUEUE_TO_MRT:
  await this.manualReviewToolService.enqueue({
    orgId,
    payload: { kind: 'DEFAULT', item, reportHistory: [], ... },
    enqueueSource: 'RULE_EXECUTION',
    enqueueSourceInfo: { kind: 'RULE_EXECUTION', rules: rules.map(x => x.id) },
    correlationId,
    policyIds: policies.map(it => it.id),
  });

以 lock 取出 Job

async dequeueNextJob(opts: {
  orgId: string;
  queueId: string;
  userId: string;
}): Promise<{ job: ManualReviewJob; lockToken: string } | null>

提交 Decisions

async submitDecision(opts: SubmitDecisionInput): Promise<SubmitDecisionResponse>

Actions

Rule match 或內容審查員提交 Decision 時會執行 Actions。

Action types 如下。

  • CUSTOMER_DEFINED_ACTION:向 platform infrastructure 傳送 POST webhook
  • ENQUEUE_TO_MRT:傳送至 Review Console
  • ENQUEUE_TO_NCMEC:路由至 NCMEC reporting Queue

Webhook structure

{
  "item": { "id": "...", "typeId": "..." },
  "policies": [{ "id": "...", "name": "...", "penalty": "..." }],
  "rules": [{ "id": "...", "name": "..." }],
  "action": { "id": "..." },
  "custom": {},
  "actorEmail": "moderator@example.com"
}

Webhook delivery 失敗時,會使用 exponential backoff,最多重試五次。

Webhook Field 參考

PropertyType是否固定存在說明
itemItem一律存在應執行此 Action 的 Item
actionAction一律存在正在觸發的 Action 資訊
policiesArray<Policy>一律存在與此 Action 關聯的 Policies。多項 Rules 觸發同一 Action 時可能包含多筆
rulesArray<Rule>不一定觸發此 Action 的 Rules。由人工審查或批次處置觸發時為空
customObject不一定在 Action form 的「Body」中設定的自訂參數
actorEmailString不一定執行 Action 之 Coop 使用者的 email。由 automated Rule 或 AI 觸發時省略

Storage

Coop 使用 multi-database storage system。

  • PostgreSQL 以 ACID guarantees 儲存 configuration、Rules、users、sessions 與 Decisions
  • Redis(透過 BullMQ) 提供 Review Console Job Queues、caching 與 aggregation counters 所需的低延遲處理
  • ScyllaDB(5.2) 儲存高吞吐量寫入的 Item submission history,並以 materialized views 支援不同 access patterns
  • ClickHouse 作為 Rule executions、Actions 與 user statistics 的 analytics warehouse

PostgreSQL

提供 config、auth、Rules 與 operational data 的 ACID-compliant storage,包括下列 schemas 與資料。

  • public:orgs、users、actions、policies、item_types、banks、api_keys
  • jobs:scheduled job tracking
  • manual_review_tool:manual review Queues、Decisions、Routing Rules、comments
  • ncmec_reporting:兒少安全 NCMEC reports
  • reporting_rules:user/content reporting Rules
  • signal_service:Signal configuration
  • user_management_service:user management
  • users_statistics_service:user statistics

Redis

作為低延遲 hot cache,用途如下。

  • Review Console:BullMQ Job Queues
  • Caching:Sets、Sorted Sets、Lua scripts
  • Distributed counters

ScyllaDB

用於高吞吐量的 Item history,包括 Investigations tool 及相關 users/Items。它以 time-series 形式儲存 Item submissions,並支援多種 access patterns。

Tables 與 views 如下。

  • item_submission_by_thread:primary table
  • item_submission_by_item_id:以 Item ID lookup
  • item_submission_by_thread_and_time:以 thread 與 time range lookup
  • item_submission_by_creator:以 creator lookup

ClickHouse

作為 analytics、aggregations 與 audit trails 的 OLAP storage。

Databases 與主要 tables 如下。

  • analyticsRULE_EXECUTIONSACTION_EXECUTIONSCONTENT_API_REQUESTSITEM_MODEL_SCORES_LOG
  • Action executionsACTION_STATISTICS_SERVICE 下的 BY_ACTIONBY_RULEBY_POLICYACTIONED_SUBMISSION_COUNTS
    • MANUAL_REVIEW_TOOL 下的 ROUTING_RULE_EXECUTIONS
  • Reporting 與 Appeal statisticsREPORTING_SERVICE 下的 REPORTSAPPEALSREPORTING_RULE_EXECUTIONS
  • User-level metricsUSER_STATISTICS_SERVICE 下的 LIFETIME_ACTION_STATSSUBMISSION_STATSUSER_SCORES

Signals

Signals 是 Rules 使用的 scoring 或 evaluation functions,範圍從簡單文字比對到 third-party ML services。

Rules engine 評估需要 score 的 conditions 時會呼叫 Signals。結果會進行 memoization 與 caching,以供重複使用。Signals 會 extend shared base class,並定義 metadata 與 execution logic。

File:/server/services/signalsService

Signals Base Class

File:/server/services/signalsService/signals/SignalBase.ts

abstract class SignalBase<Input, OutputType, MatchingValue, Type> {
  abstract get id(): SignalId;
  abstract get displayName(): string;
  abstract get description(): string;
  abstract get eligibleInputs(): readonly Input[];
  abstract get outputType(): OutputType;
  abstract get supportedLanguages(): readonly Language[] | 'ALL';
  abstract get integration(): Integration | null;
  abstract getCost(): number;
  abstract run(input: SignalInput): Promise<SignalResult | SignalErrorResult>;
}

設定

User roles 如下。

  • ADMIN:完整存取權
  • RULES_MANAGER:可修改 live Rules
  • ANALYST:可查看 insights
  • MODERATOR_MANAGER:管理 MRT Queues
  • MODERATOR:審查被指派 Queues
  • CHILD_SAFETY_MODERATOR:可存取 NCMEC data
  • EXTERNAL_MODERATOR:只有 MRT view access

Permissions 如下。

  • MANAGE_ORGADMIN
  • MUTATE_LIVE_RULESADMINRULES_MANAGER
  • VIEW_MRT:所有 moderator roles
  • EDIT_MRT_QUEUESADMINMODERATOR_MANAGER
  • VIEW_CHILD_SAFETY_DATAADMINMODERATOR_MANAGERCHILD_SAFETY_MODERATOR

Authentication

Coop 支援三種 authentication methods,分別是供 programmatic access 使用的 API key authentication、session-based authentication,以及 SAML/SSO。

API Key Authentication

API keys 用於驗證存取 REST endpoints 的 programmatic requests。所有 API requests 都必須提供 x-api-key header。

  1. Middleware 取出 x-api-key header
  2. 透過 database 中的 SHA-256 hash lookup 驗證 key
  3. Key 有效時,在 request 設定 orgId,供 downstream handlers 使用
  4. Key 無效或不存在時回傳 401 Unauthorized
  • Keys 是 32-byte random values,儲存前會進行 SHA-256 hashing
  • 每個 key 的 scope 限於單一 organization
  • 系統追蹤 last-used timestamp 供 audit 使用
  • Keys 可進行 rotation,建立新 key 並停用舊 key

Files 如下。

  • Middleware:/server/utils/apiKeyMiddleware.ts
  • Service:/server/services/apiKeyService/apiKeyService.ts

Session-Based Authentication

Dashboard UI 透過 GraphQL 使用 session authentication。

  1. User 透過 GraphQL login mutation 提交 credentials
  2. Passport 的 GraphQLLocalStrategy 驗證 email/password
  3. 透過 bcrypt comparison 驗證 password
  4. 驗證成功後,使用 passport.serializeUser() 將 user serialized 至 session
  5. 透過 connect-pg-simple 將 session 儲存於 PostgreSQL

Session configuration 如下。

  • Store:PostgreSQL-backed
  • Cookie:production 啟用 Secure flag,30-day expiry
  • Session secret:process.env.SESSION_SECRET

Files:/server/api.ts

SAML/SSO Authentication

Enterprise SSO 使用 SAML,並由各 organization 分別設定。

  1. User 前往 /saml/login/{orgId}
  2. Passport 的 MultiSamlStrategy 取得該 organization 的 SAML settings
  3. User redirected 至設定的 SAML provider
  4. Provider 完成 authentication,並將 assertion POST 至 callback URL
  5. 系統從 SAML assertion 取出 user email
  6. Lookup user record 並建立 session

各 organization 的 configuration 儲存於 org_settings table。

  • saml_enabled:Boolean flag
  • sso_url:SAML entry point URL
  • cert:供驗證使用的 certificate

Files 如下。

  • /server/api.ts
  • /server/services/SSOService/SSOService.ts

安全與維運提醒

  • 本頁記錄的是來源 commit 所描述的 architecture。實際部署、權限與資料流仍應以同一版本 code、migrations 與 runtime configuration 共同核對
  • Webhook callbacks 需依 API Keys 與 Authentication 驗證 signature,並將 handler 設計為冪等,以承受重試
  • Audit trails、Review Console Jobs、reports、Appeals、Signals 與 metrics 可能含個人資料、敏感內容或內部判斷,應分別設定最低權限、retention、刪除與 log access controls
  • VIEW_CHILD_SAFETY_DATA 等 role/permission 設定仍需由 backend enforcement 驗證,不應只依靠 UI 隱藏功能
  • Multi-database architecture 需要跨 PostgreSQL、Redis、ScyllaDB 與 ClickHouse 設計一致性、備份、復原、retention 與資料刪除流程
  • Queue lock、retry、worker restart 與 downstream callback failure 都應納入 integration tests 與 production monitoring

API Keys 與 Authentication

向 Coop 傳送 request

若要驗證傳送至 Coop 的 request,請在每一個 API request 加入含有組織 API key 的 HTTP header。API key 可在 Coop UI 的 SettingsAPI Keys 查看或管理。

Header 格式如下。

X-API-KEY: <<apiKey>>
Content-Type: application/json

API key 隨時可在同一頁輪替。輪替後,請更新所有仍使用舊 key 的應用程式或 script。

驗證來自 Coop 的 request

若要確認傳入 Action APIs 或其他 webhook 的 request 確實由 Coop 傳送,可以驗證 request signature。Coop 會簽署傳送至 endpoint 的每一個 HTTP request,並將 signature 放入 header。驗證時需使用 webhook signature verification key,也就是 public key。

  • Webhook signature verification key 顯示於 SettingsAPI Keys 的「Webhook Signature Verification Key」。需要時可在該處產生新 key。輪替後,請使用新的 public key 更新驗證邏輯。

使用 signature header 驗證 request

Coop 會將 signature 放在 Coop-Signature header,部分 client 會將名稱顯示為 coop-signature。驗證傳入 HTTP request 的步驟如下。

  1. 使用 SHA-256 對 request body 進行 hash。輸入必須是未經處理的原始 request body bytes。
  2. Coop-Signature header 的值進行 Base64 decode,取得原始 binary signature。
  3. 使用 public key 驗證 signature。Coop 使用 RSASSA-PKCS1-v1_5SHA-256。以 public key 驗證 signature,並確認結果符合第一步取得的 hash。請使用程式語言提供的 cryptography library,例如 Web Crypto、OpenSSL 或標準 crypto package,執行 RSASSA-PKCS1-v1_5 驗證。

範例(JavaScript / Node)

// Your public signing key in PEM format (from Settings → API Keys)
const pem = `-----BEGIN PUBLIC KEY-----
...your key...
-----END PUBLIC KEY-----`;

const pemHeader = '-----BEGIN PUBLIC KEY-----';
const pemFooter = '-----END PUBLIC KEY-----';
const publicKeyPem = pem.substring(
  pemHeader.length,
  pem.length - pemFooter.length,
);

const publicKeyBuffer = Buffer.from(publicKeyPem, 'base64');
const requestBodyBuffer = Buffer.from(req.body, 'utf8');
const signature = Buffer.from(req.headers['coop-signature'], 'base64');

const publicKey = await crypto.subtle.importKey(
  'spki',
  publicKeyBuffer,
  { name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-256' } },
  false,
  ['verify'],
);

const isValid = await crypto.subtle.verify(
  'RSASSA-PKCS1-v1_5',
  publicKey,
  signature,
  requestBodyBuffer,
);

請依 server 實際收到 request 的方式,調整 header 名稱(coop-signatureCoop-Signature)與 body encoding。

實作安全提醒

  • 驗證時應使用 JSON parsing、字元編碼轉換或正規化前的原始 request body bytes
  • Signature 驗證成功前,不得執行 Action 或處理 webhook 內容
  • 目前文件中的 signature 格式沒有 timestamp 或 nonce。平台應以冪等 handler 與事件紀錄控制重試及 replay 造成的影響
  • 輪替 webhook verification key 時,應事先設計過渡期、回復方式與失敗監測
  • Log 不應記錄完整 API key、signature 或 request body
  • 上線前應以實際 web framework 驗證 raw body 的取得方式,避免 middleware 預先改寫 body 而使驗證失敗

Data Warehouse Abstraction Layer

概覽

Data warehouse abstraction 讓 analytics writes 不與特定 backend 綁定。Coop 內建 ClickHousePostgreSQL adapters,也可實作 IDataWarehouse interface 支援其他 backends。只需變更一項 environment variable,即可指定 warehouse settings。

快速開始

import { inject, type Dependencies } from '../iocContainer/index.js';

class MyService {
  constructor(private readonly dataWarehouse: Dependencies['DataWarehouse']) {}

  async getUserData(userId: string, tracer: SafeTracer) {
    return this.dataWarehouse.query(
      'SELECT * FROM users WHERE id = :1',
      tracer,
      [userId],
    );
  }
}

export default inject(['DataWarehouse'], MyService);

設定

使用 WAREHOUSE_ADAPTER 與選用的 ANALYTICS_ADAPTER 選擇 adapters。Legacy deployments 可繼續使用 DATA_WAREHOUSE_PROVIDER,目前仍會作為 fallback 接受。

PostgreSQL

WAREHOUSE_ADAPTER=postgresql
ANALYTICS_ADAPTER=postgresql
# Legacy fallback:
DATA_WAREHOUSE_PROVIDER=postgresql
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=analytics
DATABASE_USER=postgres
DATABASE_PASSWORD=password

ClickHouse

WAREHOUSE_ADAPTER=clickhouse
# Optional: override analytics adapter
# ANALYTICS_ADAPTER=clickhouse
# Legacy fallback:
DATA_WAREHOUSE_PROVIDER=clickhouse
CLICKHOUSE_HOST=localhost
CLICKHOUSE_PORT=8123
CLICKHOUSE_USERNAME=default
CLICKHOUSE_PASSWORD=password
CLICKHOUSE_DATABASE=analytics
CLICKHOUSE_PROTOCOL=http

# Disable analytics writes (while keeping the warehouse)
# ANALYTICS_ADAPTER=noop

運作方式

三個 interfaces

1. IDataWarehouse,raw SQL queries

await dataWarehouse.query('SELECT * FROM users', tracer);
await dataWarehouse.transaction(async (query) => {
  await query('UPDATE users SET score = :1', [100]);
  await query('INSERT INTO audit_log VALUES (:1)', [userId]);
});

2. IDataWarehouseDialect,type-safe Kysely queries

const kysely = dialect.getKyselyInstance();
await kysely.selectFrom('users').selectAll().execute();

3. IDataWarehouseAnalytics,bulk writes 與 logging

await analytics.bulkWrite('RULE_EXECUTIONS', [
  { ds: '2024-01-01', ts: Date.now(), org_id: 'org1', ... }
]);

Loggers 的運作方式

所有 analytics loggers 都使用 abstraction

// server/services/analyticsLoggers/RuleExecutionLogger.ts
class RuleExecutionLogger {
  constructor(
    private readonly analytics: Dependencies['DataWarehouseAnalytics'],
  ) {}

  async logRuleExecutions(executions: any[]) {
    await this.analytics.bulkWrite('RULE_EXECUTIONS', executions);
  }
}

export default inject(['DataWarehouseAnalytics'], RuleExecutionLogger);

執行流程

  1. Service 呼叫 logger.logRuleExecutions(data)
  2. Logger 呼叫 analytics.bulkWrite('RULE_EXECUTIONS', data)
  3. 使用 ClickHouse 時,透過 HTTP 進行分批 JSONEachRow inserts,預設每批 500 rows
  4. 使用 PostgreSQL 時,先 buffers,再使用 COPY 或 batch INSERT

Loggers 不包含 warehouse-specific code,只需呼叫 bulkWrite()

Data flow

ClickHouse / PostgreSQL,direct

RuleExecutionLogger
    ↓
DataWarehouseAnalytics.bulkWrite()
    ↓
ClickhouseAnalyticsAdapter / PostgresAnalyticsAdapter
    ↓
HTTP JSONEachRow (Clickhouse) or batched INSERT (PostgreSQL)
    ↓
Analytics tables

必要 tables

所有 warehouses 都需要下列 tables。Schema types 定義於 /server/storage/dataWarehouse/IDataWarehouseAnalytics.ts

Core tables

  • RULE_EXECUTIONS:Rule evaluation logs
  • ACTION_EXECUTIONS:內容治理 Action logs
  • ITEM_MODEL_SCORES_LOG:ML model prediction logs
  • CONTENT_API_REQUESTS:API request logs

ClickHouse DDL 與其他 migrations 放在 db/src/scripts/clickhouse/。Schema 演進時,應在該處新增 files。

Migration 範例

ClickHouse

CREATE TABLE rule_executions (
  ds Date,
  ts UInt64,
  org_id String,
  rule_id String,
  passed UInt8,
  result String,  -- JSON as string
  -- ... ~20 more fields
) ENGINE = MergeTree()
PARTITION BY ds
ORDER BY (ds, ts, org_id);

PostgreSQL

CREATE TABLE rule_executions (
  ds DATE,
  ts BIGINT,
  org_id VARCHAR(255),
  rule_id VARCHAR(255),
  passed BOOLEAN,
  result JSONB,
  -- ... ~20 more fields
) PARTITION BY RANGE (ds);

完整 schema 請參考 /server/storage/dataWarehouse/IDataWarehouseAnalytics.ts 第 23 至 140 行。行號會隨 source 變更,使用時應以 interface 內容為準。

實作 custom warehouse

第 1 步,實作 IWarehouseAdapter plugin

server/plugins/warehouse/adapters 下建立 warehouse adapter。

// server/plugins/warehouse/adapters/MyWarehouseAdapter.ts
import type SafeTracer from '../../../utils/SafeTracer.js';
import type { IWarehouseAdapter } from '../IWarehouseAdapter.js';
import {
  type WarehouseQueryFn,
  type WarehouseQueryResult,
  type WarehouseTransactionFn,
} from '../types.js';

export class MyWarehouseAdapter implements IWarehouseAdapter {
  readonly name = 'my-warehouse';

  constructor(
    private readonly client: SomeWarehouseClient,
    private readonly tracer?: SafeTracer,
  ) {}

  start(): void {
    // Optional: warm up connection pools
  }

  async query<T = WarehouseQueryResult>(
    sql: string,
    params: readonly unknown[] = [],
  ): Promise<readonly T[]> {
    const execute = async () => {
      const rows = await this.client.execute(sql, params);
      return rows as readonly T[];
    };

    return this.tracer
      ? (this.tracer.addActiveSpan(
          { resource: 'my-warehouse.query', operation: 'query' },
          execute,
        ) as Promise<readonly T[]>)
      : execute();
  }

  async transaction<T>(fn: WarehouseTransactionFn<T>): Promise<T> {
    return this.client.transaction(async () =>
      fn((statement, parameters) => this.query(statement, parameters)),
    );
  }

  async flush(): Promise<void> {}

  async close(): Promise<void> {
    await this.client.close();
  }
}

第 2 步,提供 IDataWarehouseDialect(Kysely)implementation

若需要 type-safe queries,請建立 dialect wrapper。具體範例可參考 ClickhouseKyselyAdapter,並由 DataWarehouseFactory.createKyselyDialect 回傳。

第 3 步,實作 IAnalyticsAdapter plugin

Analytics adapters 位於 server/plugins/analytics/adapters,需實作 bulk writes 與選用的 CDC。

// server/plugins/analytics/adapters/MyAnalyticsAdapter.ts
import type { IAnalyticsAdapter } from '../IAnalyticsAdapter.js';
import {
  type AnalyticsEventInput,
  type AnalyticsQueryResult,
  type AnalyticsWriteOptions,
} from '../types.js';

export class MyAnalyticsAdapter implements IAnalyticsAdapter {
  readonly name = 'my-analytics';

  constructor(private readonly client: SomeWarehouseClient) {}

  async writeEvents(
    table: string,
    events: readonly AnalyticsEventInput[],
    _options?: AnalyticsWriteOptions,
  ): Promise<void> {
    if (events.length === 0) {
      return;
    }
    await this.client.insert(table, events);
  }

  async query<T = AnalyticsQueryResult>(
    sql: string,
    params: readonly unknown[] = [],
  ): Promise<readonly T[]> {
    return (await this.client.query(sql, params)) as readonly T[];
  }

  async flush(): Promise<void> {}

  async close(): Promise<void> {
    await this.client.close();
  }
}

第 4 步,在 DataWarehouseFactory 註冊 provider

更新 DataWarehouseFactory.createDataWarehousecreateKyselyDialectcreateAnalyticsAdapter,讓它們建立新 plugins 的 instances。Factory 會將 plugins 包在 bridges 中,application 其他部分只需使用 generic interfaces。

第 5 步,建立 analytics tables

所有 warehouses 都需要相同 tables,schema 位於 IDataWarehouseAnalytics.ts

-- Adapt syntax for your warehouse
CREATE TABLE rule_executions (
  ds DATE,
  ts BIGINT,
  org_id VARCHAR,
  item_id VARCHAR,
  rule_id VARCHAR,
  passed BOOLEAN,
  result JSON,  -- Or JSONB, String depending on warehouse
  -- ... see IDataWarehouseAnalytics.ts for all ~20 fields
);

第 6 步,設定並執行

export WAREHOUSE_ADAPTER=your-warehouse
# Optional overrides
# export ANALYTICS_ADAPTER=your-warehouse
# Legacy fallback:
# export DATA_WAREHOUSE_PROVIDER=your-warehouse
export YOUR_WAREHOUSE_HOST=localhost
# ... other config vars

npm start

Services 如何取用 analytics data

Services 使用 DataWarehouseDialect 查詢 analytics data。

// server/services/analyticsQueries/UserHistoryQueries.ts
class UserHistoryQueries {
  constructor(private readonly dialect: Dependencies['DataWarehouseDialect']) {}

  async getUserRuleExecutionsHistory(orgId: string, userId: string) {
    const kysely = this.dialect.getKyselyInstance();

    return kysely
      .selectFrom('RULE_EXECUTIONS')
      .where('ORG_ID', '=', orgId)
      .where('ITEM_CREATOR_ID', '=', userId)
      .selectAll()
      .execute();
  }
}

export default inject(['DataWarehouseDialect'], UserHistoryQueries);

適用於所有支援的 warehouses

  • ClickHouse:使用 ClickhouseDialect
  • PostgreSQL:使用 PostgresDialect

可用的 IOC Services

ServiceType用途
DataWarehouseIDataWarehouseRaw SQL、transactions
DataWarehouseDialectIDataWarehouseDialectType-safe queries
DataWarehouseAnalyticsIDataWarehouseAnalyticsBulk writes、logging

File structure

server/storage/dataWarehouse/
├── IDataWarehouse.ts              # Core interface
├── IDataWarehouseAnalytics.ts     # Analytics interface + schema types
├── DataWarehouseFactory.ts        # Instantiates adapters via env configuration
├── ClickhouseAdapter.ts           # 📝 Stub - implement this
├── ClickhouseAnalyticsAdapter.ts  # 📝 Stub - implement this
├── PostgresAnalyticsAdapter.ts    # 📝 Stub - implement this
└── index.ts

server/plugins/warehouse/           # Pluggable warehouse adapters
├── examples/NoOpWarehouseAdapter.ts
└── ...

server/plugins/analytics/           # Pluggable analytics adapters
├── examples/NoOpAnalyticsAdapter.ts
└── ...

server/services/analyticsLoggers/   # Warehouse-agnostic loggers
├── RuleExecutionLogger.ts         # Uses DataWarehouseAnalytics
├── ActionExecutionLogger.ts       # Uses DataWarehouseAnalytics
├── ItemModelScoreLogger.ts        # Uses DataWarehouseAnalytics
└── ...

server/services/analyticsQueries/   # Warehouse-agnostic queries
├── UserHistoryQueries.ts          # Uses DataWarehouseDialect
├── ItemHistoryQueries.ts          # Uses DataWarehouseDialect
└── ...

參考資料

  • Schema types/server/storage/dataWarehouse/IDataWarehouseAnalytics.ts
  • ClickHouseserver/plugins/warehouseserver/plugins/analytics adapters
  • PostgreSQL migrationsdb/src/scripts/api-server-pg/ 是 application database。Analytics tables 可依部署方式放在專用 analytics database
  • Loggers/server/services/analyticsLoggers/
  • Queries/server/services/analyticsQueries/

資料安全與切換提醒

  • Warehouse credentials 應由 secret manager 提供,不得 commit 或輸出至一般 application logs
  • ANALYTICS_ADAPTER=noop 會停用 analytics writes。啟用前應清楚記錄資料缺口的開始與結束時間,並確認依賴 analytics 的功能如何降級
  • Raw SQL 需使用 adapter 提供的 parameter binding,不可將外部輸入直接拼入 query string
  • Custom adapter 必須明確定義 transaction、retry、timeout、partial bulk failure、flush 與 shutdown semantics,不能假設所有 backends 與 PostgreSQL 相同
  • 在 ClickHouse 與 PostgreSQL 之間切換前,應完成 schema parity、歷史資料回填、雙寫或停機切換、row count reconciliation、query result comparison 與 rollback 計畫
  • Analytics tables 可能包含使用者識別、內容治理 Decision、model score 與 API request metadata,需設定最低權限、encryption、retention、partition lifecycle、刪除與 audit controls
  • Migration 或 schema 變更應與 application version 一起驗證。文件中的範例欄位、行號及 file structure 仍需以來源 commit 的實際 code 為準

Docker Images

每次 push 至 main 時,都會將預先建置的 images 發布至 GitHub Container Registry。

ghcr.io/roostorg/coop-server       # API server
ghcr.io/roostorg/coop-worker       # Background worker
ghcr.io/roostorg/coop-client       # Frontend (nginx)
ghcr.io/roostorg/coop-migrations   # One-shot database migrations runner

Images 使用 latest、Git SHA 與 release 的 semver tags。coop-migrations image 只在 db/ 下的內容變更時,例如加入 migration script,或發布 release 時重新建置,因此 tags 追蹤的是 schema,不是每次 server 或 client 變更。

快速開始

取得已發布 images,並以單一 command 在本機執行完整 stack。

docker compose -f docker-compose.images.yaml up -d

Warning

docker-compose.images.yaml 使用 .env.docker。此檔案提供可供本機評估的預設值,但包含 placeholder secrets。任何非本機部署都必須先檢查並更換 secrets。

此 command 會啟動下列 components。

  • Coop server,port 8080
  • Coop client(nginx),port 3000
  • PostgresRedisScyllaDBClickHouse
  • Database migrations,自動執行
  • Seed service,建立具有隨機 password 的 admin user

取得登入 credentials

Seed service 會將產生的 credentials 輸出至 logs。

docker compose -f docker-compose.images.yaml logs seed

在底部尋找類似下列 output。

============================================
  Login:    admin@coop.local
  Password: <randomly-generated>
============================================

接著開啟 http://localhost:3000 並登入。

使用相同 volumes 再次啟動時,seed service 會偵測既有使用者並略過,credentials 維持相同。若遺失 password,可使用 -v 移除 volumes 並重設,但此操作會清除資料。

建立其他使用者

docker compose -f docker-compose.images.yaml exec server \
  node bin/create-org-and-user.js \
  --name "My Org" \
  --email "you@example.com" \
  --website "https://example.com" \
  --firstName "Jane" \
  --lastName "Doe" \
  --password "your-password"

停止服務

# Stop containers, keep data volumes
docker compose -f docker-compose.images.yaml down

# Stop containers and wipe all data
docker compose -f docker-compose.images.yaml down -v

Caution

down -v 會刪除 Compose 管理的 data volumes。執行前應確認目前目錄、Compose file 與資料是否可重建或已有備份。

Image 詳細資訊

ImageDockerfileBuild targetBase
coop-serverDockerfilebuild_servernode:24-bullseye-slim + dumb-init
coop-workerDockerfilebuild_worker_runnernode:24-bullseye-slim + dumb-init
coop-clientclient/Dockerfileservenginx:1.27-bookworm
coop-migrationsdb/Dockerfilefinal stagenode:24-bullseye-slim

Client image 透過 nginx 提供由 Vite 建置的 SPA,並將 /api/ requests,包括 /api/v1/graphql,proxy 至名為 server、port 8080 的 backend service。

執行 migrations

coop-migrations image 將 db/ 下的 migration scripts 與 migrator engine 一起封裝,可作為 one-shot task 執行,例如 ECS RunTask、Kubernetes Job,或 docker-compose.images.yaml 中的 migrations service。它提供與本機開發相同的 npm run db:* commands,因此呼叫方式與從 repository root 執行一致。

# Apply all pending migrations to an existing prod database
docker run --rm --env-file .env.docker ghcr.io/roostorg/coop-migrations:latest \
  npm run db:update -- --db api-server-pg --env prod

支援的 --db values 為 api-server-pgscyllaclickhouse。Connection settings 來自 environment variables,見 db/.env.example。任何 command 啟動時都會讀取 database configs,因此必須提供這些設定。

--env 控制的範圍

--env 可為 stagingprod,只影響 seed scripts,不影響 migrations。

  • Migration scripts 會在所有 environments 執行
  • 名稱為 *.seed.<env>.sql 的 seed 只在 --env 相符時執行。Seed files 以 timestamp 為 prefix。此英文來源版本記載 repository 包含 db/src/scripts/api-server-pg/2025.12.01T00.00.01.initial-test-data.seed.staging.sql,會建立具有預設 password users 的 sample organization。因此,production 必須使用 --env prod 以略過。如果對 production database 使用 --env staging,會寫入測試資料
  • db:create 在功能上忽略 --env,也允許在 prod 執行,可用於自行託管 Scylla/ClickHouse 的 schema provisioning,這些服務沒有代管的 create database 步驟
  • db:cleandb:drop 是 destructive commands,會拒絕 --env prod,作為安全保護

部署提醒

  • 正式環境不要使用 latest tag,應固定經驗證的 Git SHA 或 release tag,並保留 image provenance 與 rollback 版本
  • .env.docker、Compose logs 與 shell history 可能暴露 secrets。正式環境應使用 secret manager,並限制 log 存取
  • Migration image 會對真實資料庫執行變更。執行前應備份、核對目標、檢視 migration、確認 rollback 或 forward-fix 計畫
  • --env prod 只影響 seeds,不代表 command 或設定本身安全。仍需分離帳號、network、credentials 與核准流程
  • 本文件翻譯不代表正式環境部署已通過安全、容量、備援、監控、備份或復原驗證

部署

Coop 可在自行管理的基礎設施上執行。另請參考 Docker Images架構

Important

執行 migrations 時,系統會建立一個含有預設密碼使用者的範例組織。Production 環境務必清除這些帳號與資料。

Self-hosting 檢查清單

Coop 目前沒有提供單一的 production deployment recipe,但 repository 已包含建立 self-hosted instance 所需的設定項目。請以 db/.env.exampleserver/.env.exampleclient/.env.example 的範例環境檔為起點,建立符合自身部署環境的設定。

Production 必要設定

Production 部署至少需要提供下列項目。

  • API server Postgres instance 與 database migrator 的 database connection
  • Queue 與 background processing 使用的 Redis connection
  • Item submission history 使用的 Scylla connection
  • SESSION_SECRETGRAPHQL_OPAQUE_SCALAR_SECRET 等 session 及 token secrets
  • UI_URL / VITE_UI_URL 等公開 UI origin,讓產生的連結與 browser-facing flow 指向正確 host
  • 符合部署環境的 email sender addresses

正式上線前,通常也需要檢查 server/.env.example 中的 pool、timeout、TLS 與 keepalive 設定。預設值以 local development 為目標,不一定適合長期執行的 production 環境。

選用及依部署而定的設定

許多設定只在啟用特定功能或更換 backend 時才需要。

  • Analytics 與 warehouse backend 由 WAREHOUSE_ADAPTERANALYTICS_ADAPTER 控制。支援的 adapter 及相關 ClickHouse/PostgreSQL 設定,請參考 Data Warehouse Abstraction Layer
  • 兒少安全通報為選用功能。若使用 NCMEC 通報,必須先完成 Coop 的組織設定,且只有在部署已獲准進行正式通報後,才可在 server 設定 NCMEC_ENV=production。詳見 NCMEC CyberTipline 的測試與正式提交
  • Google Places、custom docs/content proxy URLs 等 client-side integrations 為選用項目,不使用相關功能時可留空。
  • server/.env.example 中的 third-party integration keys 通常只有在啟用對應 integration 時才需要設定。

正式上線前

第一次成功完成 migration 與 bootstrap 後,請依序確認下列事項。

  1. 移除或妥善保護範例組織,以及所有使用預設密碼建立的使用者。
  2. 確認 production hostname 與 email 設定正確。
  3. 確認 warehouse 及 analytics adapters 與實際部署的 backing services 一致。
  4. 除非確定要傳送正式 CyberTipline 通報,否則應讓 NCMEC_ENV 保持未設定或使用非 production 值。

Single Sign-on

Coop 支援透過 SAML 使用 single sign-on,例如 Okta。請在 Settings → Single Sign-on 啟用 SSO,並設定 URL 與 certificate。

範例:Okta

為 Coop 設定 Okta SAML 需要下列條件。

  • 可使用 Okta admin mode
  • Okta 與 SAML 的 group names 完全一致
  • 具備 Coop admin 權限
  • 能夠建立 custom SAML application

設定步驟如下。

  1. 在 Okta 建立 custom SAML application,並使用下列設定。

    設定
    Single sign-on URL組織的 callback URL,例如 https://your-coop-instance.com/login/saml/12345/callback。可在 Coop 的 SettingsSSO 找到。
    Audience URI (SP Entity ID)Coop instance 的 base URL,例如 https://your-coop-instance.com
    email attribute(位於 Attribute Statementsemail。實際值取決於 identity provider 的 attribute mappings,例如 Google SSO 可能使用「Primary Email」。
  2. Feedback tab 勾選 I’m a software vendor. I’d like to integrate my app with Okta

  3. 前往 app settings 的 Sign On tab,在 SAML Signing CertificatesSHA-2 下點選 ActionsView IdP metadata

  4. 複製 XML file 的內容。在 Coop 前往 SettingsSSO,將 XML 貼入 Identity Provider Metadata field。

  5. 在同一頁的 Attributes section 輸入 email

  6. 在 Okta app 的 Assignments 下,將 users 或 groups 指派給 app。

歷史參考資料

先前 production deployment 使用的 AWS infrastructure code,包括 CDK、Helm charts、Pulumi 與 CDKTF,可在 0.1 tag 找到。該 infrastructure code 已不再維護,也可能與目前 application architecture 有落差,但仍可作為自行部署的參考。

Production readiness 補充檢查

  • 固定 container images 與 dependencies 的版本,並保留 artifact provenance 及更新程序
  • 使用 secret manager 管理 production secrets,不將 secrets 放入 repository 或一般設定檔
  • 完成 network segmentation、TLS、最低權限 access、backup、restore test、monitoring 與 alerting
  • Migration 前建立可驗證的 backup,並準備 rollback 或 forward-fix 程序
  • 驗證 queue、workers、upstream outage、service restart 與 disaster recovery 情境
  • 對兒少安全通報及其他敏感資料設定獨立的存取控制、稽核紀錄與 retention policy
  • 為 SSO certificate rotation、break-glass access 與 identity provider outage 準備程序
  • 0.1 tag 的 infrastructure code 僅供歷史參考,不應未經審查就直接部署

API 參考

本節介紹 Coop API,包括平台用來與 Coop 整合的 REST API endpoints。

所有 endpoints 都要求每次 request 透過 HTTP header 傳送 API key。

X-API-KEY: <<apiKey>>
Content-Type: application/json

您可以從 Coop 使用者介面的 SettingsAPI Keys 查找或輪替 API key。驗證 Coop 加在外送 webhook request 上的 signature,見 API Keys 與 Authentication

Endpoint說明
POST /api/v1/items/async/Items,傳送內容供 Rule 評估
POST /api/v1/reportReport,提交使用者檢舉
POST /api/v1/report/appealAppeal,提交使用者申訴
GET /api/v1/policies/Policies,取得已設定的 Policies

另請參閱下列文件。

  • 處理 Actions,接收 Coop 對自動 Action、內容審查員 Decision、使用者違規門檻及申訴 Decision 送出的 webhooks
  • Partial Items API,讓 Coop 可依需要取得 Item 及其屬性
  • Errors,了解 Coop error response

安全提醒

API key 應由 server-side secret 管理機制提供,不得放入前端、公開文件、issue、log 或版本控制。應為不同環境使用不同金鑰、限制存取、建立輪替與撤銷程序,並監控異常 request。

Submit Items API

Item 傳送至 Coop,進行自動 Rule 評估。每次提交 Item,Coop 都會使用所有已設定的主動式規則進行評估。

Item 建立、編輯、遭檢舉或需要重新評估時,都應提交。如果上線後才設定新 Rule,也應追溯提交既有 Item。若要讓 Coop 依需要取得 Item 及其屬性,見 Partial Items API

Endpoint

POST /api/v1/items/async/

驗證使用 X-API-KEY header。詳情見 API Keys 與 Authentication

Request

{
  "items": [
    {
      "id": "unique-item-id-123",
      "typeId": "your-item-type-id",
      "data": {
        "fieldName1": "value1",
        "fieldName2": 123
      }
    }
  ]
}

items Array 加入其他 Objects,即可於單一 request 提交多個 Items。所有處理皆為非同步。

Request body Fields

FieldType必要性說明
itemsArray必要要提交的一個或多個 Items
items[].idString必要平台為 Item 使用的唯一識別碼
items[].typeIdString必要從資訊儀表板設定之 Item 的 Coop Item Type ID
items[].dataObject必要Item payload。Fields 必須符合 Item Type 定義的 schema
items[].data.imagesArray選填URL strings 的 Array,會觸發自動 HMA 圖片雜湊
items[].typeVersionString選填供 schema versioning 使用的 version string
items[].typeSchemaVariantString選填Schema variant,有效值為 "original""partial"

data Fields 格式

data 的形狀必須符合 Item Type schema。常見 Field types 如下。

Field type格式
String一般 string value
NumberJSON number
Booleantruefalse
Image/Audio/Video指向媒體的 URL string
GeohashBase-32 geohash string
DatetimeISO 8601 string,例如 "2024-01-15T10:30:00.000Z"
Related Item{ "id": "...", "typeId": "..." } object

媒體存取

所有媒體 Fields,包括 image、audio 與 video,都以 URL references 提交。Coop 不會直接上傳或保存媒體內容。

Item 提交時,Coop 會立即取得每個媒體 URL,執行 HMA hashing、Content Safety analysis 等 Signal 處理。內容審查員開啟 Job 時,Coop 不會重新取得媒體,改由瀏覽器直接從原始 URL 載入。

Coop 以未驗證的 GET requests 取得媒體,不會將 API key 或其他憑證轉送至媒體 URL。

若媒體需要驗證或受存取控制,請使用 pre-signed URL,例如 S3 pre-signed URL。由於內容審查員可能在提交數小時或數天後才開啟 Job,瀏覽器會在審查時直接載入媒體,因此 pre-signed URL 必須在 Job 可能停留於 Queue 的完整期間內有效,不能只涵蓋提交時的 Signal 處理時間。

Response

Status意義
202 Accepted已收到 Items,並排入 Rule 評估 Queue
400 Bad Request驗證失敗,見 Errors
401403驗證失敗

完整 error response 格式見 Errors

自動圖片雜湊

若 Item 的 data Object 包含由 URL strings Array 構成的 images Field,Coop 會自動進行下列步驟。

  1. 從提供的 URLs 取得圖片內容
  2. 為每張圖片計算感知雜湊
  3. 與組織已設定的所有 HMA Matching Banks比對
  4. 將產生的 HMA Signals 加入 Item,供自動 Rules評估

注意事項

  • 非同步處理:此 endpoint 為大量非同步處理設計。Submission 會進入 Redis Queue,透過 BullMQ 交由 background workers 處理
  • 立即結果:實作若嚴格要求同步處理,也就是在相同 HTTP response 收到 Rule results,可使用舊版 POST /api/v1/content/ endpoint。舊版 endpoint 不支援 batch submissions 或自動 HMA 圖片雜湊
  • Action Callbacks:Rule 符合並觸發 Action 時,Coop 會對處理 Actions所述的 callback endpoint 送出 POST request
  • 基本概念:Item Types 與 Coop 如何識別 Items,見基本概念

安全與隱私提醒

  • Pre-signed URL 有效期間愈長,遭轉寄或洩漏後的風險愈高。應限制可存取物件、HTTP method、來源、有效期與記錄,並在 Job 關閉後撤銷或失效
  • 審查員瀏覽器會直接連接媒體來源,可能暴露網路 metadata,也可能受到不受信任檔案影響。媒體服務應使用隔離網域、正確 Content-Type、下載限制與惡意檔案防護
  • 202 Accepted 只表示進入 Queue,不代表 Rule 評估或 Action 已完成。平台應另行監測 background worker、失敗與 callback 結果
  • 新 Rule 上線後追溯提交大量 Item 前,應先小批次測試成本、重複 Action、Rate limit 與復原方式

Report API

將使用者檢舉提交至 Coop。平台收到使用者標記後,透過此 API 傳送,在 Review Console 建立內容治理 Job。

Coop 如何處理 Report、Routing Rules 與 NCMEC 的完整流程,見檢舉

Endpoint

POST /api/v1/report

驗證使用 X-API-KEY header。詳情見 API Keys 與 Authentication

Request

{
  "reporter": {
    "kind": "user",
    "typeId": "reporter-user-type-id",
    "id": "reporter-user-id"
  },
  "reportedAt": "2024-01-15T10:30:00.000Z",
  "reportedForReason": {
    "policyId": "violated-policy-id",
    "reason": "Free-text reason from reporter",
    "csam": false
  },
  "reportedItem": {
    "id": "reported-item-id",
    "data": { "fieldName": "value" },
    "typeId": "item-type-id"
  },
  "reportedItemThread": [
    {
      "id": "thread-message-1",
      "data": { "content": "message content" },
      "typeId": "message-type-id"
    }
  ],
  "reportedItemsInThread": [
    { "id": "specific-reported-message", "typeId": "message-type-id" }
  ],
  "additionalItems": [
    { "id": "additional-context-item", "data": {}, "typeId": "item-type-id" }
  ]
}

Request body Fields

FieldType必要性說明
reporterReporter必要提交 Report 的使用者
reportedAtDatetime必要Item 遭檢舉時間的 ISO 8601 timestamp
reportedItemReportedItem必要遭檢舉的 Item
reportedItem.data.imagesArray選填URL strings 的 Array,會觸發自動 HMA 圖片雜湊
reportedForReasonReportedForReason選填Item 遭檢舉的原因
reportedItemThreadArray<ReportedItem>選填同一 Thread 中的其他 Items,例如私訊 Thread 中的前後訊息。Coop 用來向內容審查員顯示完整脈絡
reportedItemsInThreadArray<ItemIdentifier>選填reportedItemThread 中明確遭檢舉的 Items,會在審查使用者介面標記
additionalItemsArray<ReportedItem>選填與 Report 一起顯示的補充內容,例如作者近期貼文

Reporter schema

FieldType必要性說明
kindString必要檢舉實體類型,目前只支援 "user"
idString必要平台對檢舉使用者使用的唯一識別碼
typeIdString必要從 Item Types 資訊儀表板設定的檢舉使用者 Item Type ID

ReportedItem schema

FieldType必要性說明
idString必要平台對遭檢舉 Item 使用的唯一識別碼
typeIdString必要遭檢舉 Item 的 Item Type ID
dataJSON必要Item payload,必須符合 Item Type 定義的 schema

reportedItemThread 使用與 ReportedItem 相同的 schema,但不嚴格強制必要 Fields,以支援追溯取得資料。Thread Items 應包含 datetime Field,確保正確依時間排序。

ItemIdentifier schema

FieldType必要性說明
idString必要平台對 Item 使用的唯一識別碼
typeIdString必要Item 的 Item Type ID

ReportedForReason schema

FieldType必要性說明
policyIdString選填若檢舉者選擇的原因已對應 Policy,填入違反 Policy 的 ID
reasonString選填檢舉者說明提交原因的自由格式文字
csamBoolean選填設為 true 時,Coop 將 Job 直接送至 NCMEC Queue,不進入預設審查 Queue

Response

成功時回傳由 Coop 指派的唯一 Report ID。

{ "reportId": "report-uuid" }
FieldType說明
reportIdStringCoop 指派給 Report 的唯一 ID

HTTP statuses 如下。

Status意義
201 Created已收到 Report,回傳 reportId
400 Bad Request驗證失敗,見 Errors
401403驗證失敗

完整 error response 格式見 Errors

資料與路由提醒

  • reportedItemThreadadditionalItems 會擴大內容審查員可見資料。只應提供判斷必要的前後文,不要預設傳送整個私訊歷程或作者全部近期內容
  • reason 可能含誹謗、威脅、健康、性或其他敏感資訊,應限制長度、處理惡意輸入,並避免顯示給無關人員
  • csam: true 會略過一般 Routing Rules 並進入高敏感工作流程。平台應限制哪些檢舉原因與系統可設定此值,並監測誤用
  • 201 Created 代表 Report 已建立,不代表內容違規、已完成審查或已向任何機關通報

Appeal API

將使用者申訴提交至 Coop。使用者對平台的內容治理 Decision 提出異議時,透過此 API 傳送申訴,並在 Review Console 建立審查 Job。

申訴如何顯示、維持或推翻 Decision 的完整流程,見申訴。內容審查員對申訴作成 Decision 後,Coop 會透過 Appeal Decision callback 將結果傳送至平台。

Endpoint

POST /api/v1/report/appeal

驗證使用 X-API-KEY header。詳情見 API Keys 與 Authentication

Request

{
  "appealId": "platform-internal-appeal-id",
  "appealedBy": {
    "typeId": "appealer-user-type-id",
    "id": "appealer-user-id"
  },
  "appealedAt": "2024-01-15T12:00:00.000Z",
  "actionedItem": {
    "id": "item-that-was-actioned",
    "data": { "fieldName": "value" },
    "typeId": "item-type-id"
  },
  "actionsTaken": ["action-id-1", "action-id-2"],
  "appealReason": "User's explanation for why they are appealing",
  "violatingPolicies": [{ "id": "policy-id-1" }, { "id": "policy-id-2" }],
  "additionalItems": [
    { "id": "additional-context-item", "data": {}, "typeId": "item-type-id" }
  ]
}

Request body Fields

FieldType必要性說明
appealIdString必要平台內部的 Appeal submission ID。內容審查員處理申訴時,會將此值傳回平台
appealedByItemIdentifier必要提出申訴的使用者,包括平台內部 user ID 與該使用者的 Coop Item Type ID
appealedAtDatetime必要提交申訴時間的 ISO 8601 timestamp
actionedItemItem必要原本遭執行 Action 的 Item
actionsTakenArray<String>必要已執行並送至 Action callback 的 Action Coop IDs
appealReasonString選填使用者說明申訴原因的自由格式文字
violatingPoliciesArray<Policy>選填最初內容治理 Action 執行時,從 Action webhook 收到的 Policies
additionalItemsArray<Item>選填與申訴一起顯示、提供脈絡的補充內容

Response

Status意義
204 No Content已成功收到 Appeal
400 Bad Request驗證失敗,見 Errors
401403驗證失敗

完整 error response 格式見 Errors

資料與流程提醒

  • appealReasonadditionalItems 應限制為複核必要內容,不應把整個帳號或無關對話歷程一併提交
  • appealId 應具唯一性,平台也應避免同一 Appeal 因重試而建立重複 Job
  • Callback 成功後,仍需由平台向使用者提供結果、理由與後續救濟資訊,並正確處理原 Action 的反向操作

Policies API

以程式方式取得組織已設定的 Policies。

Endpoint

GET /api/v1/policies/

驗證使用 X-API-KEY header。詳情見 API Keys 與 Authentication

Response

{
  "policies": [
    { "id": "policy-id-1", "name": "Violence", "parentId": null },
    {
      "id": "policy-id-2",
      "name": "Graphic Violence",
      "parentId": "policy-id-1"
    },
    { "id": "policy-id-3", "name": "Threats", "parentId": "policy-id-1" },
    { "id": "policy-id-4", "name": "Spam", "parentId": null }
  ]
}

Response Fields

FieldType說明
policiesArray組織的全部 Policies
policies[].idStringCoop 為 Policy 建立的唯一且不可變 ID
policies[].nameString使用者為 Policy 指定的顯示名稱
policies[].parentIdString 或 null上層 Policy 的 ID,頂層 Policy 則為 null

注意事項

  • 使用 parentId 重建完整 Policy tree。parentIdnull 代表頂層 Policy,非 null 值則將子 Policy 連至上層
  • 整合應使用 Policy id,不要使用 name。名稱可從資訊儀表板變更,ID 則不可變
  • Policy 結構與用途見基本概念管理與設定

處理 Actions

Coop 透過自動 Rule、Review Console 內容審查員 Decision,或使用者超過 User Strike 門檻而觸發 Action 時,會對該 Action 所設定的 callback URL 傳送 POST request。平台 server 收到 request 後,再執行對應操作。

設定 callback endpoint

在 Coop 定義每個 Action 時,需提供公開可存取的 callback URL,以及 endpoint 所需的 authentication headers,例如由 Coop 傳送的 API key。Coop 會在傳送至該 endpoint 的每個 request 加入這些 headers。

若要確認新進 request 確實由 Coop 傳送,請檢查 Coop-Signature header。Signature verification algorithm 與程式碼範例見 API Keys 與 Authentication

傳送失敗時,Coop 會使用 exponential backoff,最多重試五次。

重要實作要求 Callback 可能因 timeout、網路中斷或 response 遺失而重複傳送。平台必須使用穩定的事件識別或業務條件,將 Action handler 設計為冪等。驗證 Coop-Signature 失敗時不得執行 Action,也不得只以來源 IP 取代簽章驗證。

Request body

{
  "item": { "id": "item-id", "typeId": "item-type-id" },
  "action": { "id": "action-id" },
  "policies": [{ "id": "policy-id", "name": "Spam", "penalty": "MEDIUM" }],
  "rules": [{ "id": "rule-id", "name": "Spam detector" }],
  "custom": {},
  "actorEmail": "moderator@example.com",
  "creator": { "id": "user-id", "typeId": "user-type-id" },
  "decisionReason": "Violated spam policy",
  "userStrikeCount": 3
}

Field 參考

FieldType是否固定存在說明
itemItem一律存在應執行此 Action 的 Item
actionAction一律存在正在觸發的 Action
policiesArray<Policy>一律存在與此 Action 相關的 Policies。多個 Rules 觸發相同 Action 時,可能包含多筆
rulesArray<Rule>不一定觸發此 Action 的 Rules。由人工審查或批次處置觸發時為空
customObject不一定在 Action 表單 Body 中設定的自訂參數。對遭檢舉 Item 的 Review Console Decision,Coop 也會將 reasonreportHistory 合併至此 Object,見下方 Custom Object
actorEmailString不一定執行 Action 之 Coop 使用者的 email。自動 Rule 觸發時省略
actorNoteString不一定內容審查員執行 Action 時加入的 note,未填寫時省略
creatorItemIdentifier不一定Action 所涉及的使用者。USER Item 使用目標本身,CONTENT Item 使用內容作者。無法解析 creator 時省略,例如只有 content ID 且沒有已知 submission
decisionReasonString不一定內容審查員從 Review Console 或 Submit Decision API 提供的 Decision reason。Review Console Decision 有填寫 reason 時存在,其他情況省略
userStrikeCountNumber不一定此 Action 後使用者的累計違規分數,包括既有分數及本次事件加入的分數。無法解析目標使用者時省略

Item schema

FieldType說明
idString平台為 Item 使用的唯一識別碼
typeIdStringItem Type ID
typeNameStringItem Type 顯示名稱

Policy schema

FieldType說明
idStringCoop 唯一 Policy ID
nameStringPolicy 名稱
penaltyStringPenalty level,可為 NONELOWMEDIUMHIGHSEVERE

Rule schema

FieldType說明
idStringCoop 唯一 Rule ID
nameStringRule 名稱

Custom Object

除了在 Body 中設定的參數,對遭檢舉 Item 作成 Review Console Decision 時,Coop 也會將下列資訊合併至 custom。Decision reason 同時出現在頂層 decisionReason Field,因此會在兩處出現。

FieldType說明
reasonString內容審查員的 Decision reason,與頂層 decisionReason 相同
reportHistoryArray<Report>對 Item 提出的 Reports,每筆格式為 { reason, reporter }

User Strikes

使用者累計違規分數超過設定門檻時,Coop 會使用相同 callback 機制,執行與該門檻關聯的 Action。與 Rule 觸發 callback 的差異如下。

  • policies 一律為空 Array,因為門檻依累計分數觸發,不代表此次 request 的特定 Policy 違規
  • rules 一律為空 Array,沒有 Rule 直接觸發 callback
  • actorEmailactorNote 一律不存在,因為沒有人工執行者

User Strikes、門檻及關聯 Actions 的設定方式,見使用者指南的 User Strikes

Appeal Decision callback

內容審查員在 Review Console 審查 Appeal 並作成 Decision 後,Coop 會對 Appeals Dashboard 設定的 Appeal callback URL 傳送 POST request。

{
  "appealId": "your-appeal-id",
  "item": { "id": "item-id", "typeId": "item-type-id" },
  "appealedBy": { "id": "user-id", "typeId": "user-type-id" },
  "appealDecision": "ACCEPT",
  "custom": {}
}

Appeal callback Field 參考

FieldType是否固定存在說明
appealIdString一律存在平台內部 Appeal ID,與透過 Appeal API 傳送的值相同
itemItem一律存在原本遭執行內容治理 Action 的 Item
appealedByItemIdentifier一律存在提交 Appeal 的使用者
appealDecisionString一律存在ACCEPT 代表原始 Action 不正確並接受 Appeal,REJECT 代表維持原始 Action
customObject不一定Appeal Configuration Form 的 Body 中設定的自訂參數

完整 Appeal submission 流程見申訴

安全、隱私與可靠性提醒

  • 驗證簽章時應使用原始 request body,並以 Coop authentication 文件與實作指定的演算法為準。目前 signature 格式沒有 timestamp 或 nonce,重試與 replay 的影響需由冪等 handler 與事件紀錄控制
  • actorEmaildecisionReasonactorNotereportHistory 可能包含個人資料、敏感內容或內部判斷,callback endpoint 應採最低權限、加密傳輸、欄位最小化與受控 logging
  • Callback URL 與 headers 由管理設定提供,應限制可接受目的地,避免錯送 secrets,並防範 server-side request forgery
  • 平台應在成功完成 Action 後才回傳 2xx。若採非同步處理,需先可靠寫入自己的 Queue,再回傳成功
  • Appeal ACCEPT 後,平台仍需確認反向 Action 成功、更新使用者狀態並傳送結果通知,不能只記錄 callback

Partial Items API

讓 Coop 可從平台取得 Item 及其詳細資訊。此 API 用於回填歷史資料、在 Review Console 取得尚未傳送至 Coop 的相關 Items,以及確保查看時使用最新 Item 資料。

設定 endpoint

若要使用此 API,平台必須提供可接收 Coop POST requests 的 Partial Items API endpoint。Coop 需要平台上特定 Item 的資訊時,會將該 Item 唯一 ID 傳送至此 endpoint。

若要確認新進 request 確實由 Coop 傳送,請檢查 Coop-Signature header。Signature verification algorithm 與程式碼範例見 API Keys 與 Authentication

Important

依此英文來源版本,尚無法從 Coop 使用者介面設定此功能,必須從程式碼管理。詳情見 roostorg/coop#378

您需要在 Coop instance 程式碼設定 endpoint URL 與所有必要 headers,包括 API key 等其他 authentication,讓 Coop 可發出 HTTP requests。

REST API 範例

以下是 Coop 會傳送至 endpoint 的 POST request 範例。

curl --request POST \
    --url https://your-platform.example.com/partial-items \
    --header 'Content-Type: application/json' \
    --header 'Coop-Signature: t=1234567890,v1=5f7d8e9...' \
    --data '{
        "items": [
            {
                "id": "abc123",
                "typeId": "def456"
            },
            {
                "id": "xyz789",
                "typeId": "def456"
            }
        ]
    }'

Request body 只有一個頂層 property items,是代表 Coop 需要補充資訊之 Items 的 Objects Array。使用 Array 是為了讓 Coop 可依需要,在單一 API request 中批次要求多個 Items。

items Array 中每個 Object 需包含下列 Fields。

PropertyType說明
idString平台為 Item 使用的唯一識別碼
typeIdString對應 Item 的 Item Type ID,必須完全符合平台已定義的其中一個 Item Type ID

Response 要求

Endpoint 必須回傳 2xx status 及 JSON body,其中包含一個具有 items Array 的頂層 Object。可包含其他頂層 keys,但 Coop 會忽略,只處理 items

items 中每個 entry 描述 Coop 所要求的其中一個 Item。平台可以回傳 Item 的 partial 版本,data 只包含可取得的 Fields 子集,但下列 keys 必須存在且 type 正確。

PropertyType必要性說明
idString必要Coop 在 request body 提供的 id
typeIdString必要Coop 在 request body 提供的 typeId
dataObject必要與 Items API 傳送內容相同的形狀。可為空 Object {},但 key 必須存在
typeVersionString選填指定目標 Item Type version
typeSchemaVariant"original" | "partial"選填預設為 partial

Response 範例如下。

{
  "items": [
    {
      "id": "abc123", // the `id` Coop provided
      "typeId": "def456", // the `typeId` Coop provided
      "data": {
        // the same shape you'd send via the Items API
        "text": "some text uploaded by a user"
        // ... all other fields in your Item Type
      }
    }
  ]
}

若找不到或無法回傳特定 Item,請從 items Array 省略,不要回傳 error 或 sentinel value。Coop 不會把缺少 Item 視為失敗,只會沒有該 Item 的資料。Request 中沒有出現相同 (id, typeId) 的 Items 會被無聲捨棄。

另一種可接受的格式,是將 typeIdtypeVersiontypeSchemaVariant 放在 type Object 中,分別使用 idversionschemaVariant。新整合建議使用上述扁平格式,巢狀格式只為了與 Items API submission 形狀保持一致。

{
  "items": [
    {
      "id": "abc123",
      "data": { "text": "..." },
      "type": {
        "id": "def456",
        "version": "2025-01-01",
        "schemaVariant": "partial"
      }
    }
  ]
}

疑難排解

Webhook logs 中有 request,但 Coop 內 Item 沒有更新時,使用者介面會顯示下列其中一種錯誤。

  • PartialItemsEndpointResponseError:endpoint 回傳非 2xx status
  • PartialItemsInvalidResponseError:body 可解析為 JSON,但不符合上述 schema,常見原因是缺少 data、頂層 items key,或 idtypeId 不是 string;也可能完全無法解析為 JSON。Body 解析失敗時,Coop server logs 會包含 response bytes 的短 prefix。最常見原因是重複寫入 response,例如 middleware 在 payload 前先寫入 sentinel,產生 null{"items":[...]}

Request 看似成功但 Item 仍未顯示時,請確認每個回傳 Item 的 (id, typeId) 與 Coop request 完全相同。不相符的資料會被無聲捨棄。透過 tunnel 測試時,例如 localtunnelngrok,請確認 tunnel 沒有以 browser warning page 取代 JSON response。

安全、隱私與可靠性提醒

  • Partial Items endpoint 會依 Coop request 回傳平台資料。必須先驗證 Coop-Signature,再查詢或回傳 Item,並限制 request size、batch 數量與 rate
  • 即使 Coop 要求某個 Field,平台仍應只回傳此次審查與 Rule 評估所需資料。partial 不代表可以略過目的限制與權限檢查
  • Endpoint URL 與 authentication headers 目前由程式碼管理,不得硬編碼 secrets 或提交至版本控制。應透過環境變數或 secret manager 提供
  • 無聲省略找不到的 Item 方便部分回應,但可能掩蓋權限錯誤或資料同步問題。平台應在不記錄敏感內容的前提下,監測省略比例與原因
  • 回傳前應驗證 (id, typeId) 屬於要求範圍,避免物件層級授權錯誤。不可只依可猜測的 id 查詢
  • 若 Item data 含媒體 URL、私訊、位置或帳號資料,應確認 URL 權限、有效期與 Review Console 實際可見角色

Errors

本頁說明 Coop 如何回應 API requests 與 errors。

HTTP status codes

Status意義
200 OKRequest 成功,response body 包含資料
202 Accepted已收到 Item submission,並排入非同步處理 Queue
204 No ContentRequest 成功,沒有 response body,例如 Report 與 Appeal submissions
400 Bad RequestRequest 無效,包括 JSON 格式錯誤、缺少必要 Fields 或 schema 不相符,例如 Report 引用不存在的 Item。詳情見 error body
401403驗證失敗,API key 缺漏、無效或過期
429 Too Many Requests超過 rate limit
500503Server 內部錯誤,通常是暫時性問題,可安全重試
502504Gateway 或 dependency error,上游服務無法使用,請重試

Error response 格式

所有 4xx errors 都會以下列格式回傳 JSON body。

{
  "errors": [
    {
      "status": 400,
      "type": ["/errors/invalid-user-input"],
      "title": "Short error description",
      "detail": "Detailed explanation of the problem (optional)",
      "pointer": "/path/to/problematic/field (optional)",
      "requestId": "correlation-id (optional)"
    }
  ]
}
Field說明
statusHTTP status code
typeError type identifiers 的 Array
title簡短、供人閱讀的摘要
detail補充 error 脈絡,若有
pointer指向造成 error 之 Field 的 JSON pointer,若適用
requestId供 tracing 使用的 correlation ID

整合提醒

  • 500502503504429 重試時,應使用有上限的 exponential backoff 與 jitter,避免服務中斷時放大流量
  • 只有具備冪等性,或平台使用唯一 request ID 防止重複處理的操作,才能安全自動重試
  • 對外顯示錯誤時,不應暴露 secrets、內部 stack trace、個人資料或敏感 Item 內容
  • 保存 requestId 可協助調查,但 log 仍應採資料最小化與存取控制

整合

Coop 可連接 Google Content Safety API、OpenAI Moderation API 與 Zentropi CoPE 等安全服務 API。Coop 已內建多種整合,設定時需輸入相應 API key。

服務狀態提醒 下列費用、資格與服務條件來自本繁中版本所記錄的英文來源 commit,可能隨供應商政策調整。申請或導入前,請重新查閱供應商的官方條款、資料處理說明與最新價格。

內建整合

各項整合的詳細資訊與要求,請參閱對應文件。

整合費用要求
Google Content Safety API免費API key、Google 核准1
Hasher-Matcher-Actioner(HMA)免費自有雜湊,以及/或第三方 Hash Bank 存取權2
NCMEC Reporting免費CyberTip API key、NCMEC 核准3
OpenAI Moderation API免費4OpenAI API key
Zentropi CoPE免費,另有付費選項5Zentropi API key

Model cards

每項整合都有 model card,以一致且可比較的方式說明運作方式。Model card 是描述機器學習模型預定用途、行為與限制的短文件,可視為 AI 分類器的營養標示。

各整合的 model card 說明下列欄位。

欄位說明
Purpose模型設計要偵測或分類的內容
Input模型接受的內容類型,例如圖片、文字或網址
Output模型回應的格式與意義
Limitations已知缺漏、失敗模式或難以處理的內容類型
Requirements使用整合所需的存取、核准或設定
Best practices取得可靠結果的建議

自動分類器都可能出錯。Model card 可協助判斷 Signal 在何種情況下可供參考、哪些結果需要人工審查,以及如何合理設定 Rule。


  1. 希望保護平台免受濫用的產業與公民社會第三方,可申請 Content Safety API 存取權。申請時請提及使用 Coop 審查工具。申請需經核准,並接受 Google 條款及細則。

  2. 使用自有 Hash Bank 不需額外憑證或授權,例如組織自行保存的已知違規內容集合。使用各第三方雜湊則需取得該組織授權。例如,NCMEC 需提供 Hash Sharing API 憑證,Tech Against Terrorism 也會管理其 Hash Bank 存取權。

  3. 需要完成 NCMEC ESP registration,並經核准取得 CyberTip API 憑證。

  4. OpenAI 說明,Moderation API 免費,且不計入每月用量限制。

  5. Zentropi 文字分類器可免費使用,目前採用開放授權的 CoPE-A-9B 模型。API 支援自行建立的所有 labeler,包括免費及選用付費功能。詳情見 Zentropi 價格說明

Google Content Safety API

Content Safety API 是 AI 分類器,會對傳送至服務的內容提供 Child Safety 優先順序建議。

要求

Content Safety API 使用者必須自行進行人工審查,才能決定是否對內容採取 Action,並遵守所在地適用的通報法律。

希望保護平台免受濫用的產業與公民社會第三方,可申請 Content Safety API 存取權。申請時請提及使用 Coop 審查工具。申請需經核准,並接受 Google 條款及細則。

Coop 目前支援透過 Google Content Safety API 分類下列檔案格式的圖片。

  • BMP
  • GIF
  • ICO
  • JPEG
  • PNG
  • PPM
  • TIFF
  • WEBP

回應

回應會包含五種優先順序中的一種。

Priority ENUM
VERY_LOW
LOW
MEDIUM
HIGH
VERY_HIGH

優先順序愈高,圖片愈可能是虐待內容。然而,這只是一項指標,並非確認結果。您必須一律進行人工審查,以確認內容並避免誤判。 因此,這項 Signal 只能用於人工 Routing Rules,不能用於自動 Action Rules。

良好實務

  • 為取得最佳效能,建議圖片解析度約為 640 × 480 像素,約 30 萬像素
  • 圖片小於 30 萬像素時,請勿放大,因為放大會引入雜訊,也不會改善效能
  • 圖片大於 30 萬像素時,可考慮縮小至 30 萬像素,預期不會降低效能
  • 一般建議使用能維持品質的 codec 壓縮圖片,例如 WEBP 或品質 90 以上的 JPEG,以減少 request 大小

限制

  • 一次最多可傳送 32 張圖片
  • 圖片必須採用上述格式之一
  • JSON body 總大小不得超過 10 MB
  • 最大 QPS 為 200

台灣使用提醒

  • API 優先順序不能取代人工判斷、法律認定或通報決策
  • 將疑似兒少性影像傳送至外部 API 前,應確認申請資格、服務條款、資料所在地、保存與再利用方式,以及是否允許傳送實際個案資料
  • 一般功能測試應使用合成或經核准的安全測試素材,不得使用真實 CSAM 驗證整合

Hasher-Matcher-Actioner(HMA)

Coop 整合 Meta 的開放原始碼 Hasher-Matcher-Actioner(HMA),針對已知 CSAM、非自願私密影像(NCII)、恐怖主義與暴力極端主義內容(TVEC),以及自行維護的 Hash Bank,提供感知雜湊比對。

雜湊比對會計算提交媒體的感知指紋,圖片使用 PDQ,影片使用 MD5,再與已知有害內容資料庫比對。與 AI 分類器相比,和 NCMEC 等經驗證資料庫中的雜湊符合,是強而可靠的 Signal。已知內容即使經過輕微修改,仍可可靠比對。

要求

  1. Coop 伺服器可存取、正在執行的 HMA instance
  2. 要使用之第三方 Hash Bank 的 API 憑證。例如,NCMEC 提供 Hash Sharing API 憑證,Tech Against Terrorism 則管理其 Hash Bank 存取權
  3. 自有 Hash Bank,選填,例如組織自行保存的已知違規內容集合
  4. 在 Coop 的 SettingsIntegrations 設定 HMA 服務網址

連線後,HMA Signals 會顯示在 Coop Signal 函式庫,可供 Routing Rules 與 Proactive Rules 使用。

管理 Hash Banks

Hash Bank 是已知有害媒體指紋的集合,可供 Rule 引用。您可以透過 Coop 使用者介面建立及管理 Bank,也可從 NCMEC 等外部來源同步。

Matching Banks 頁面,顯示在 Coop 使用者介面建立的測試 Hash Bank。

透過 Coop 建立 Bank

建議從 Coop 的 SettingsMatching Banks 建立 Bank。Coop 會自動在 HMA 與 Coop 資料庫登記,立即可於 Rule builder 使用。

透過 Coop 建立的 Bank,會依 COOP_<ORGID>_<NORMALIZED_NAME> 慣例在 HMA 命名。例如,組織 abcdef12345 的「Test Bank」會成為 COOP_ABCDEF12345_TEST_BANK,這也是 HMA 使用者介面顯示的名稱。

直接在 HMA 建立 Bank

直接透過 HMA 使用者介面或 seed scripts 建立的 Bank,不會顯示在 Coop Matching Banks 使用者介面,除非也登記於 hash_banks table。若需要在 Coop Rule 使用 HMA 原生 Bank,請先從 Coop 使用者介面建立對應 Bank。

HMA 使用者介面顯示從 Coop 建立的 Bank,以及手動上傳媒體至 Matching Bank 時出現的視窗。

無論 Bank 從何處建立,都可以使用 HMA 使用者介面手動加入內容,供本機測試。

NCMEC Hash Sharing

若要讓 Coop 與 NCMEC 的已知 CSAM 雜湊資料庫比對,需要 NCMEC Hash Sharing API 憑證。

在 HMA 建立以 NCMEC exchange 為來源的 Bank。HMA 會依背景擷取排程開始同步雜湊,預設每五分鐘一次。同步完成後,NCMEC 來源 Bank 會顯示在 Coop Matching Banks

詳情見 NCMEC CyberTipline 整合

在 Rules 中使用 HMA Signals

HMA 連線並設定 Hash Banks 後,圖片雜湊 Signal 可同時用於 Routing Rules 與 Proactive Rules。

  • 若要路由來自使用者 Report 的內容,請建立含雜湊比對邏輯的路由規則,並送至所需 Queue。若是 NCMEC 符合項目,應送至已設定的 NCMEC Queue

    使用 HMA 雜湊比對的 Routing Rule。

  • 對透過 Items API 提交的內容,若希望 Coop 在沒有使用者 Report 時主動計算雜湊並標記符合項目,請建立含圖片雜湊條件及「Enqueue to NCMEC」Action 的主動式規則

另請參閱

台灣使用提醒

  • 雜湊符合只能證明計算結果與資料庫項目相符,實際意義仍取決於資料庫品質、來源授權、hash type 與資料同步狀態
  • 自有及第三方 Bank 應有明確來源、加入與移除程序、存取控制、稽核紀錄及誤比對處理方式
  • 不應為測試而上傳真實 CSAM 或未經同意的私密影像。使用安全測試指紋或由資料提供者核准的測試集合
  • Proactive Rule 直接搭配「Enqueue to NCMEC」前,應先確認錯誤路由、人工複核、資料彙整及跨境傳輸後果

NCMEC CyberTipline

重要適用提醒 本頁是 Coop 與美國 NCMEC CyberTipline 技術整合的翻譯,不構成台灣法律意見。請勿只依本文件啟用正式通報。組織應先由具資格的法律、兒少安全、資安與事件應變人員確認申請資格、資料處理權限、測試方式、保存義務、跨境傳輸、通報對象與失敗處理。

Coop 整合 National Center for Missing and Exploited Children(NCMEC)的 CyberTipline Reporting API。Coop 處理完整生命週期,包括透過雜湊比對或 AI Rule 偵測已知 CSAM、將內容送至專用 NCMEC 人工審查 Queue,以及提交含相關 metadata 的 CyberTip。

內容審查員工作流程見兒少安全通報

要求

開始審查並向 NCMEC 通報內容前,需要具備下列條件。

  1. 完成 NCMEC Electronic Service Provider(ESP)registration 並取得核准
  2. CyberTipline API 憑證,包括 NCMEC 提供的 username 與 password,用來向 CyberTipline API提交 Report
  3. 具有 creatorId Field 的 User Item Type。NCMEC Job 以使用者為中心,不以個別內容為中心。Coop 透過內容 Item 的 creatorId Field 取得使用者。該 Field 是參照 User Item Type 的 RELATED_ITEM,接著彙整與該使用者相關的所有媒體,建立單一 NCMEC 審查 Job
  4. 專用 NCMEC 人工審查 Queue,供 Coop 路由 Job。Queue 中的 Decision 會送出真實 CyberTip 或進入 NCMEC sandbox,由 Coop server 的 NCMEC_ENV environment variable 控制,詳情見測試與正式提交
  5. Additional Info endpoint,選填但強烈建議。Coop 在提交 CyberTip 前呼叫此 webhook,取得電子郵件、screen name、IP capture events 與每項媒體的詳細資訊。未設定時,只會使用 user ID 與 Item data 中的基本資訊提交
  6. Preservation endpoint,選填。CyberTip 成功提交後,Coop 呼叫此 webhook,讓平台依 NCMEC 要求保存相關使用者資料。Coop 沒有內建資料保存功能,只會在設定後呼叫所提供的 endpoint
  7. 在 Coop 的 SettingsNCMEC 完成上述 NCMEC 組織設定,詳情見下方 NCMEC settings

雜湊比對(HMA)

若要透過雜湊比對自動偵測已知 CSAM,需要 NCMEC Hash Sharing API 憑證。請在 HMA curator 使用者介面設定,或在 HMA service 使用 TX_NCMEC_CREDENTIALS environment variable。

詳情見 Hasher-Matcher-Actioner(HMA)整合

NCMEC settings

請從 SettingsNCMEC Settings 設定 NCMEC 通報。

在 Coop 設定 NCMEC Reporting,加入向 NCMEC 提交違反組織 Policy 內容所需資訊。

設定說明
UsernameNCMEC CyberTipline API username
PasswordNCMEC CyberTipline API password
Company Report Name組織在 NCMEC Report 中使用的名稱,也會在每份 CyberTip 中作為遭通報使用者的 ESP service name
Legal URL服務條款或法律政策網址,例如 https://yourcompany.com/terms
Contact EmailCyberTip 通報人的電子郵件。NCMEC 傳回的 XML receipt 可作為 ESP 通知
Terms of Service與遭通報事件相關的 TOS 文字或 acceptable use policy 網址,最多 3,000 字元
Contact Person (for law enforcement)執法機關可聯繫的人員,與通報聯絡電子郵件分開,包括名字、姓氏、電子郵件與電話
More Info URL通報流程補充資訊網址,例如 https://yourcompany.com/ncmec-info。當 Default Internet Detail Type 設為 Web page 時,作為 web page URL
Default NCMEC Queue審查員點選 Enqueue to NCMEC 後,Job 送至此 Queue。保留 Use org default queue 時,改用組織預設 Queue
Default Internet Detail Type每份 CyberTip 包含的事件脈絡或媒介,包括 Web page、Email、Newsgroup、Chat/IM、Online gaming、Cell phone、Non-internet 或 Peer-to-peer
NCMEC Additional Info Endpoint提交 CyberTip 前,Coop 呼叫此 webhook 取得補充使用者與媒體 metadata。詳情見 Additional Info endpoint。強烈建議設定,否則只會提交最低限度的使用者資料
NCMEC Preservation EndpointCyberTip 成功提交後,Coop 會將 Report ID 傳送至此 webhook。詳情見 Preservation endpoint

保存 credentials、Company Report Name 與 Legal URL 後,會為組織啟用 NCMEC Reporting。其他 Fields 並非提交 CyberTip 的必要欄位,但填寫後可讓 NCMEC 調查人員更容易採取行動

將內容路由至 NCMEC

內容可透過三種自動偵測路徑與一種人工路徑送至 NCMEC Queue。人工升級及內容進入 Queue 後的彙整方式,見兒少安全(NCMEC)

1. 雜湊比對已知 CSAM(HMA)

Coop 整合 Meta 的 Hasher-Matcher-Actioner(HMA),將上傳媒體與 NCMEC 已知 CSAM 雜湊資料庫比對。這是最可靠的偵測路徑。雜湊符合是內容被確認為已知 CSAM 的強 Signal。

HMA 透過 NCMEC Hash Sharing API同步雜湊,讓平台在本機取得 NCMEC 已知 CSAM 圖片與影片指紋資料庫,以快速比對。

詳情見 HMA 整合

2. 新出現的 CSAM 偵測(Content Safety API)

對於先前未見、沒有已知雜湊的內容,Coop 整合 Google Content Safety API,分類圖片是否可能為 CSAM。您可以建立使用 Content Safety Signal 的 Routing Rule,將高信心結果直接送至 NCMEC Queue,也可先送至初步分類 Queue,經人工審查後再升級。

詳情見 Google Content Safety API

3. 標記為 CSAM 的新進 Report

平台將使用者 Report 傳送至 Coop Report API,並設定 reportedForReason.csam: true 時,Coop 會自動送至 NCMEC Queue,不使用預設審查 Queue。這些 reason 應由平台 Report 流程設定,並與平台定義的檢舉理由一致。

{
  "reporter": { "id": "user123", "typeId": "user-type-id" },
  "reportedAt": "2025-01-01T00:00:00Z",
  "reportedItem": {
    "id": "content456",
    "typeId": "post-type-id",
    "data": { ... }
  },
  "reportedForReason": {
    "csam": true
  }
}

4. 人工升級

在任何審查 Job 中,具有 NCMEC 存取權的內容審查員都可從 Action 清單選擇 Enqueue to NCMEC,立即將 Job 移至 NCMEC Queue。

CyberTip 提交流程

審查員提交 CyberTip 時,Coop 依序進行下列步驟。

  1. 取得補充資訊:呼叫 Additional Info endpoint,取得使用者電子郵件、screen name、IP capture events 與每項媒體的詳細資訊

  2. 建立 CyberTip XML:彙整完整 Report

    • escalateToHighPriority:boolean,將 Report 標示為高優先順序,例如虐待正在發生,供 NCMEC 分類時優先處理
    • Incident summary:審查員選擇的 incident type,以及最近建立媒體項目的 timestamp,作為 incidentDateTime
    • Internet details:事件發生的 channel 或 medium,例如 Web page、Chat/IM、Email,由 NCMEC 組織設定中的 Default Internet Detail Type 決定
    • Reporter:組織名稱(companyTemplate)、Legal URL、Contact Email、選填的 Terms of Service 文字,以及選填的執法機關聯絡人,全部來自 NCMEC 組織設定
    • Reported user(personOrUserReported:疑似行為者。Coop 包含下列資訊
      • espIdentifier:使用者在平台內部的 ID
      • espService:組織名稱,來自 companyTemplate
      • screenName:使用者 username,來自 Additional Info endpoint
      • displayName:使用者 display name,若有
      • email:已知電子郵件地址,來自 Additional Info endpoint
      • ipCaptureEvent:與使用者相關的 IP addresses,例如登入與上傳事件,來自 Additional Info endpoint。提供 IP 資料會顯著改善 NCMEC 辨識及定位嫌疑人的能力。若 User Item 的 Item Type 將 ipAddress 對應至 schema Field role,該 IP 也會以 Unknown event 及 Report incident time 加入
    • Victim:若能辨識兒少被害人,例如來自 messaging 脈絡,Coop 會加入其 espIdentifierscreenNamedisplayNameipCaptureEvent,協助 NCMEC 定位並提供協助
  3. 提交 Report:Coop 以 POST 將 Report XML 送至 NCMEC CyberTipline API,並取得 reportId

  4. 上傳媒體:Coop 針對每項媒體,從網址下載檔案並上傳至 NCMEC,附上完整檔案 metadata

    • Industry classification(A1/A2/B1/B2)
    • File annotations,審查員選擇的 labels
    • 與上傳相關的 IP capture events。若 Media Item 的 Item Type 將 ipAddress 對應至 schema Field role,該 IP 也會以 Upload event 及媒體 createdAt 加入
    • 內容是否曾在平台公開,publiclyAvailable
    • ESP 是否查看檔案與 EXIF data,fileViewedByEsp: trueexifViewedByEsp: true
    • Additional Info endpoint 提供的 file hash,若有
  5. 上傳補充檔案:Additional Info endpoint 回傳的其他檔案,例如螢幕截圖與佐證資料,會以 supplemental reported files 上傳至 NCMEC

  6. 上傳 message threads:若使用者在 messaging 脈絡遭到 Report,Coop 會為每個 conversation thread 建立 CSV 並上傳至 NCMEC

  7. 完成 Report:呼叫 NCMEC /finish endpoint 完成提交

  8. 保存 Report:將完成的 Report,包括 Report ID、XML 與所有媒體詳細資訊,保存至 Coop database

  9. 傳送 preservation request:若組織設定 Preservation endpoint,Coop 會將 Report ID 傳送至該 endpoint,供平台保存相關使用者資料

測試與正式提交

每份 CyberTip 會依 Coop server 的 NCMEC_ENV environment variable,送至兩個 NCMEC endpoints 之一。

  • 未設定,或值不是 production:使用 NCMEC test endpoint,Report 會由 NCMEC 丟棄,是整合測試的安全預設值
  • NCMEC_ENV=production:使用 NCMEC production endpoint,Report 會進入調查流程,例如送交執法機關

營運者有責任確認 NCMEC_ENVSettingsNCMEC 設定的 credentials 相符。NCMEC 分別提供 test 與 production credentials。未經核准的整合若提交至 production endpoint,可能導致 credentials 遭撤銷。

送至 test endpoint 的 Report 會以 is_test flag 保存於 Coop database,且只對提交者顯示在 NCMEC Reports 資訊儀表板。Production Report 則會顯示給組織內所有具有 VIEW_CHILD_SAFETY_DATA 權限的人。

正式環境防護提醒 NCMEC_ENV=production 會造成實際外部通報與資料傳輸。變更此設定前,應採至少雙人確認、核對 credentials 與組織核准狀態,並以不含真實 CSAM 的安全測試資料完成 test endpoint 驗證。

Webhooks

Additional Info endpoint

Coop 在建立 CyberTip 之前呼叫此 webhook,取得遭通報使用者與媒體的補充 metadata。此 endpoint 為選填,但強烈建議設定。未設定時,只會提交 user ID 與先前透過 Items API 傳送至 Coop 的資料。

Coop 使用組織 signing key 簽署每個 request。處理前應先驗證 signature。

Request

{
  "users": [{ "id": "string", "typeId": "string" }],
  "media": [{ "id": "string", "typeId": "string" }]
}

Response

{
  "users": [
    {
      "id": "string",
      "typeId": "string",
      "screenName": "string",
      "email": [
        {
          "email": "user@example.com",
          "type": "Home",
          "verified": true,
          "verificationDate": "2025-01-01T00:00:00Z"
        }
      ],
      "ipCaptureEvent": [
        {
          "ipAddress": "192.0.2.1",
          "eventName": "Upload",
          "dateTime": "2025-01-01T00:00:00Z",
          "possibleProxy": false,
          "port": 443
        }
      ],
      "data": {}
    }
  ],
  "media": [
    {
      "id": "string",
      "typeId": "string",
      "missing": false,
      "publiclyAvailable": true,
      "fileName": "image.jpg",
      "additionalInfo": ["string"],
      "ipCaptureEvent": [
        {
          "ipAddress": "192.0.2.1",
          "eventName": "Upload",
          "dateTime": "2025-01-01T00:00:00Z",
          "possibleProxy": false,
          "port": 443
        }
      ],
      "fileDetails": {
        "hash": "abee9985862d273160d930d2ac6ddb2cc33c74e73c702bcc8183d235f6f9685a",
        "hashType": "PDQ"
      }
    }
  ],
  "additionalFiles": [
    {
      "fileUrl": "https://yourplatform.com/evidence/file.pdf",
      "fileName": "evidence.pdf",
      "additionalInfo": ["Supporting evidence"]
    }
  ],
  "messages": [
    { "id": "string", "typeId": "string", "ipAddress": "192.0.2.1" }
  ],
  "additionalInfo": "string"
}

Response Fields

FieldType說明
usersArray必須包含 request 中每位使用者的 entry
users.idString必須與 request 的 id 相符
users.typeIdString必須與 request 的 typeId 相符
users.screenNameString使用者在平台上的 screen name 或 username
users.emailArray已知電子郵件地址,type 可為 BusinessHomeWork
users.ipCaptureEventArray與使用者相關的 IP events,例如登入與註冊。eventName 可為 LoginRegistrationPurchaseUploadOtherUnknown。Coop 也會在有 schema 對應時,將 User Item 的 ipAddress Field role 以 Report incident time 的 Unknown event 加入
users.dataObject使用者的原始 Item data
mediaArray若 request 包含媒體,必須為每項媒體提供 entry
media.idString必須與 request 的 id 相符
media.typeIdString必須與 request 的 typeId 相符
media.missingBoolean媒體已無法取得時設為 true。若所有媒體都是 missing: true,不會提交 CyberTip
media.publiclyAvailableBoolean通報當時,媒體是否可在平台公開存取
media.fileNameString媒體原始檔名
media.additionalInfoArray<String>納入 NCMEC file details 的媒體補充脈絡
media.ipCaptureEventArray與媒體 Item 相關的 IP events。若有 schema 對應,Coop 也會將 Media Item 的 ipAddress Field role 以媒體 createdAtUpload event 加入
media.fileDetailsObject檔案雜湊資訊,格式為 { hash, hashType }
additionalFilesArray上傳至 NCMEC 的補充佐證檔案,例如螢幕截圖
messagesArrayConversation thread 脈絡中的 message-level IP address data
additionalInfoString納入 Report 的頂層自由格式補充資訊

Important

Response 若沒有為 request 中的每位使用者與每項媒體提供 entry,Coop 會產生錯誤,不提交 CyberTip。Endpoint 必須為每個 requested user 與 media item 回傳 entry。 若所有 media items 都是 missing: true,Coop 不會提交 CyberTip。Job 會標示為 permanent error,且不會重試。

Preservation endpoint

向 NCMEC 提交 CyberTip 的平台,可能依美國 18 U.S.C. § 2258AREPORT Act(2024)等法律負有資料保存義務。英文來源記載 REPORT Act 將內容保存要求延長至一年。請諮詢法律團隊,確認組織的具體義務。

CyberTip 成功提交後,Coop 會立即呼叫由平台建立及託管的 preservation endpoint。Endpoint 應觸發內部資料保存流程,例如將帳號標記為 legal hold、建立相關紀錄 snapshot,或通知法律團隊。Coop 會傳送遭通報使用者、CyberTip 包含的媒體與 NCMEC 指派的 Report ID,供平台識別需保存的資料。

Coop 使用組織 signing key 簽署每個 request。處理前應先驗證 signature。

Request

{
  "user": { "id": "string", "typeId": "string" },
  "reportedMedia": [{ "id": "string", "typeId": "string" }],
  "reportId": "string"
}
Field說明
user向 NCMEC 通報的使用者
reportedMediaCyberTip 包含的所有媒體 Items
reportIdNCMEC 指派的 CyberTip Report ID

Coop 只檢查 HTTP status code 是否成功,不處理 response body。此 webhook 只會在正式、非測試的 CyberTip 提交時呼叫。

台灣保存提醒 上述美國法律與一年期間不能直接套用為台灣義務。平台需要依自身適用法令與案件性質確認保存範圍、期限、存取、凍結、刪除及解除 legal hold 的程序。保存資料不代表可以無限期或無限制擴張使用目的。

重試行為

CyberTip 提交失敗時,例如短暫網路錯誤或 NCMEC API 中斷,Coop 會自動重試。背景 Job 會定期執行,重試符合下列條件的失敗 NCMEC Decisions。

  • 尚未成功提交,database 中沒有符合的 Report
  • 先前重試少於 10 次
  • 未標示為 permanent error,例如所有媒體都遺失
  • Decision 在過去 30 天內作成

上線前最低檢查

  1. 使用安全測試資料,在 NCMEC test endpoint 完整驗證路由、Additional Info、媒體缺漏、signature、Report 儲存、權限與重試
  2. 確認 test 與 production credentials 分離,且 NCMEC_ENV 變更有人工核准、紀錄與回復程序
  3. 驗證 webhook 只接受 Coop 正確簽署的 request,並設定 timeout、重送冪等性、告警與失敗處理
  4. 確認 Report XML、IP、電子郵件、媒體、EXIF、CSV 與補充檔案的存取權、加密、備份、保存與刪除
  5. 確認至少有兩位適當角色人員可處理事件,但不因此擴張不必要的兒少安全資料存取
  6. 由法律與兒少安全專業人員書面確認正式通報與 preservation 流程後,才啟用 production

OpenAI Moderation API

Coop 整合 OpenAI moderation endpoint,依有害內容類別分類文字。英文來源記載 moderation endpoint 可免費使用,導入前仍應重新確認目前服務條款與價格。

要求

  • 具有 API 存取權的 OpenAI 帳號

設定

在 Coop 前往 Settings → Integrations,加入 OpenAI API key。

Signals

每種有害內容類別都會成為 Coop Signal 函式庫中的獨立 Signal。所有 Signals 都回傳 0 至 1 的分數,可作為 Routing Rules 與 Proactive Rules 的條件門檻。

Signal偵測內容
Hate針對受保護特徵群體表達仇恨的內容
Hate (threatening)同時包含威脅或暴力語言的仇恨內容
Self-harm描繪、鼓勵或提供自傷指示的內容
Sexual露骨性內容
Sexual (minors)涉及未成年人的性內容
Violence描繪或美化暴力或身體傷害的內容
Violence (graphic)寫實或血腥暴力內容

Coop 也提供 OpenAI Whisper 轉錄 Signal,可將音訊內容轉為文字,再交由文字 Signals 進行後續分析。

Models

Coop 呼叫 /v1/moderations endpoint 時不固定 model 版本,因此 OpenAI 會套用當時的預設 model。此英文來源版本記載預設值為 omni-moderation-latest。若 OpenAI 變更預設值,Coop 行為也會自動跟著改變。

限制

  • 分數是機率結果,並非確定判斷。請使用符合平台情境的門檻,並人工審查確認的正向結果
  • omni-moderation-latest 支援圖片 input,但 Coop 目前的整合只傳送文字 Fields,圖片需要另外設定
  • 不同語言與地區的效能可能不同,模型針對英文內容最佳化

台灣使用提醒

  • 繁中內容應獨立建立測試集,評估不同書寫方式、語碼混用、諧音、反諷與在地脈絡,不能直接沿用英文門檻
  • API key 不得放入 repository、issue、前端程式碼或公開截圖
  • 傳送內容前應確認資料最小化、外部服務保存與使用方式、跨境傳輸,以及自傷與兒少內容的人工升級流程
  • 未固定 model 版本會造成輸出隨供應商預設值變化。正式使用時應監測分數分布與決策結果的漂移

Zentropi CoPE

Zentropi CoPE(Content Policy Enforcement)是可依 Policy 調整的 AI 文字分類器。不同於使用固定分類的分類器,CoPE 沒有預先定義類別。使用者自行撰寫希望偵測內容的 Policy 文字,model 再依這些 Policy 分類內容。對於具有細緻或特殊內容規範,且一般現成分類器難以處理的平台,這項特性特別實用。

此英文來源版本記載,整合使用的 model 是 CoPE-A-9B,版本 1.x,於 2025 年 7 月發布。

要求

  • 具有 API 存取權的 Zentropi 帳號
  • 在 Zentropi 使用者介面建立一個或多個 labeler version,每個都需包含 Policy 定義

設定

在 Coop 前往 Settings → Integrations,加入 Zentropi 憑證。

  • API Key:Zentropi API key
  • Labeler Versions,選填:在 Zentropi 使用者介面建立的 labeler version ID 與標籤清單。加入後,建立 Rule 時可直接依名稱選用

Signals

在 Zentropi 使用者介面建立的每個 labeler version,都會成為 Coop 的獨立 Signal。建立 Rule 條件時,選擇 Zentropi Signal,並在 subcategory 欄位輸入 labeler version ID。

Coop 將文字 Field 值送至 Zentropi API,並取得 0 至 1 的分數。

  • 0 代表 model 有信心內容安全,也就是不違反 Policy
  • 0.5 代表不確定
  • 1 代表 model 有信心內容違反 Policy

此分數可搭配 Rule 條件中的任一比較運算。例如,使用 score > 0.8,只在高信心違規時觸發。

撰寫 Policy

Zentropi 分類器在 Policy 定義採用下列結構時,表現最佳。

  1. Overview:Policy 主題簡介
  2. Definition of Terms:精確定義關鍵詞語與片語
  3. Interpretation of Language:說明如何處理模糊語言
  4. Definition of Labels:標籤包含與排除的範圍

Zentropi 文件與範例程式碼 notebook詳細說明 Policy 撰寫方式。

限制

  • 只支援文字:整合只分類文字 Fields,不支援圖片與影片
  • 8,000 token 限制:超過 8K tokens 的文字會遭截斷
  • 只支援美式英文:其他語言與地區的效能會顯著降低
  • 二元分類:每個 labeler version 回傳「violating」(1)或「not violating」(0)與信心分數,不提供中間類別或多標籤 output
  • Policy 設計會影響結果:model 無法分類需要外部查證的內容,例如連結是否惡意。訓練資料偏誤可能影響不同人口群體的分類模式,應定期監測及稽核 Decision

Model Card

欄位英文來源所列資訊
ModelCoPE-A-9B
Version1.x
Release date2025 年 7 月 20 日
Training data約 60,000 個獨特 Policy/內容組合標籤,混合自動與人工標註,涵蓋仇恨言論、性內容、自傷、騷擾與毒性
Annotation methodology針對 Policy 解釋而非記憶的新式訓練方法,使用互相衝突的 Policy 表述訓練
PerformanceHate Speech 91%(內部)、84%(公開 Ethos benchmark);Sexual Content 89%;Toxic Speech 90%;Self-Harm 88%;Harassment 73%
Compared to英文來源宣稱在多數類別優於 GPT-4o、Llama-3.1-8B、LlamaGuard3-8B 與 ShieldGemma-9B

連結

台灣使用提醒

本 model 明確標示只支援美式英文,不應用於繁中自動處置。Model card 中的效能數值與比較屬英文來源所載結果,尚未在本翻譯工作中獨立驗證,也不代表台灣繁中內容表現。即使未來有繁中 model,也需要以在地 Policy、代表性資料、不同群體與實際誤判成本重新評估。

自訂整合

Coop 支援 plugin 形式的整合。作者可將整合發布為獨立套件,例如 npm package;採用者不需修改 Coop 原始碼,只要透過設定檔啟用。平台啟動時會載入每個啟用的 plugin,並使用 manifest 提供的 metadata,包括標題、文件、標誌、model card 與設定欄位。採用者不需編輯 enums、server registries 或 client logo maps,只需安裝套件並編輯 integrations 設定檔。

給整合作者

您需要建立匯出 plugin 的套件,包括 manifest 與選用 Signals。Manifest 描述整合的 id、名稱、版本、文件連結、標誌、設定欄位,以及所提供的 Signals。標誌可以是套件內由平台提供的檔案,也可以使用網址。若整合需要每個組織個別設定,例如 API keys,請在 manifest 定義欄位,平台會產生設定表單並保存值。

完整參考實作見範例套件 coop-integration-example。其中包含 manifest、設定欄位、model card、標誌與範例 Signal。建立自訂整合時請以此為範本,contract 與 types 位於 @roostorg/coop-types

給採用者

  1. 在 server app 安裝整合套件,例如 npm install @roostorg/coop-integration-example

  2. 從 integrations 設定檔啟用。Server 預設讀取 server/integrations.config.json,也可讀取 INTEGRATIONS_CONFIG_PATH 指定的路徑。結構範例如下

    {
      "integrations": [
        { "package": "@roostorg/coop-integration-example", "enabled": true }
      ]
    }
    

    本機開發若使用本機套件,可設定為 "package": "../coop-integration-example",路徑相對於設定檔所在目錄。不要把 secrets 放入設定檔。API keys 等資料應使用應用程式內的整合設定流程。

  3. 從使用者介面使用。整合會顯示在 integrations 頁面,組織可以設定內容。若 plugin 提供 Signals,這些 Signals 會與內建 Signals 一樣顯示在 Rule builder

安全與維護提醒

  • 安裝 plugin 等同將第三方程式碼加入 Coop server。導入前應檢查來源、授權、維護狀態、相依套件、已知漏洞與發布完整性
  • 依 Coop 專案規則,新增或升級任何套件都需要人工核准,並應提交對應 lockfile 變更
  • Manifest 的外部標誌網址與文件連結會產生對外 request,應確認內容安全政策與隱私影響
  • Plugin 可取得的設定與 Item 資料應採最小權限。卸載時也需規劃 secrets、設定值、紀錄與衍生資料的清理方式

授權與翻譯聲明

本繁中文件改作自 ROOST Coop 英文文件。原始內容由 ROOST 及其貢獻者依 Apache License 2.0 提供。

繁中版本由 mashbean/coop fork 的貢獻者翻譯與在地化。所依據的英文檔案、commit 與審查狀態記錄於 sources.tsv。台灣補充不代表 ROOST 官方立場。

本版本目前尚未獲 ROOST 上游採納。若繁中內容與英文原文不一致,請以相對應來源 commit 的英文文件為準。