ROOST 開放原始碼工具 × 台灣繁體中文
讓小型社群,也能開始建立可被檢查的安全治理流程
Coop 將自動規則、人工審查、檢舉、申訴與稽核集中在同一套開放原始碼工具。這份繁中指南協助社群管理者、志工與工程團隊理解流程,再判斷哪些部分適合自己的平台。
- 37 / 37上游文件完成第一輪翻譯
- 4 道檢查用語、忠實度、建置與連結
- 版本可追溯對應固定的英文來源版本
適合拿來開始評估
- 志工型社群與論壇
- 小型內容平台
- 公民科技專案
ROOST 安全工具生態系
從偵測到執行,看見完整治理流程
不同規模的社群可以從目前最需要的環節開始。DIRE 將安全工作拆成偵測、調查、審查與執行,讓工具選擇回到實際責任與流程。
-
D
Detection偵測
安全模型、雜湊比對與平台 Signals 找出需要注意的內容或行為。
-
I
Investigation調查
Osprey 協助分析事件、關聯實體與協同行為,再建立可重複使用的規則。
-
R
Review審查
Coop 將內容送到適當 Queue,提供脈絡並保存人工判斷。
-
E
Enforcement執行
自動規則或人工決定透過 Action 回到平台,並保留申訴與稽核紀錄。
Osprey
即時處理平台事件、撰寫安全規則、查詢行為模式,協助團隊回應垃圾訊息、機器人與協同濫用。
閱讀 Osprey 繁中指南 ↗開放安全工具地圖
依雜湊比對、分類、隱私、規則引擎、審查、調查與聯邦宇宙等 14 個類別尋找可評估的工具。
瀏覽繁中工具清單 ↗ROOST 社群與治理
認識專案路線圖、參與角色、社群平台、資安與開發規範,以及文件和會議協作方式。
查看繁中社群文件 ↗自動處理完成 51 / 51 項,含 37 份第一輪譯文與 14 份高風險安全參考。
繁中第一輪完成,5 / 5 組來源。收錄社群示範與實驗,未必適合正式環境。
繁中第一輪完成,1 / 1 份。說明自訂 Signal、規則與 plugin 的工程範例。
繁中第一輪完成,1 / 1 份。把公開 Git repository 安全鏡像至其他 forge。
使用提醒 工具或模型被收錄不代表 ROOST 或繁中維護者背書。採用前仍需核對維護狀態、授權、資料處理方式、語言涵蓋率與適用條件。
依角色開始
從眼前需要處理的問題進入
不必先讀完所有技術文件。可依目前負責的治理工作,選擇最接近的入口。
完整治理迴路
一套工具,串起決策前後的責任
Coop 的價值涵蓋自動化、人工判斷、對外處置、申訴與稽核,不只提供內容分類結果。
- 01
接收內容與檢舉
平台提交 Items,使用者 Reports 帶入需要處理的脈絡。
- 02
規則評估與路由
Signals 與 Rules 協助自動處理,或將工作送入適當 Queue。
- 03
人工審查與照護
內容審查員取得必要脈絡,並使用身心健康功能降低暴露風險。
- 04
執行 Action
透過 callback 將警告、限制或其他治理決策送回平台。
- 05
申訴與稽核
保存決策紀錄、處理 Appeals,讓結果可追蹤且可被複核。
-
來源狀態
37 份 Markdown 對應固定 commit 與明確審查狀態。
-
台灣繁體中文
攔截簡體詞候選、語氣混用與已完成頁面的舊英文連結。
-
技術忠實度
核對 code blocks、inline tokens、圖片、表格與 style blocks。
-
可用頁面
完成 mdBook build,再檢查內部頁面、錨點與靜態資源。
使用前請留意
工具與翻譯都不能代替組織責任
NCMEC 是美國制度;文件內容不構成台灣法律意見,也不代表平台已完成權限、資料保存、事件應變、申訴及正式部署驗證。任何兒少安全整合測試都不得使用真實 CSAM、私密影像或可識別個案資料。
開始建立共同語言
先理解治理元件,再決定要自動化多少
從基本概念認識 Coop 的資料模型與流程,或直接查看完整目錄。
用自己的方式進行審查與內容治理。

Coop 是 ROOST 推出的開放原始碼審查與內容治理工具,為網路安全提供完整解決方案。
- 審查主控台:供人工處理複雜內容治理決策的介面
- 內容處理:支援貼文、留言、媒體與自訂內容類型
- 分析:提供內容治理成效與趨勢的詳細資訊
- 規則引擎:依照可自訂的政策自動評估內容
- API 整合:提供簡易 REST 與 GraphQL API,與平台順暢整合
Coop 適用對象
Coop 適合任何需要作成網路安全決策的人,包括各種規模的平台、獨立開發者,以及沒有專職信任與安全人員的社群團隊。
多數內容治理工具採專有授權,定價也以原本就負擔得起的平台為對象。Coop 免費且開放原始碼,讓資料保留在自己的基礎設施中,也能依社群需要自訂。
下列原則影響 Coop 的開發方式。
- 平台擁有自己的政策。 Coop 提供實作及執行自有規範所需的管線。
- 兒少安全是優先工作流程。 Coop 以成為第一套免費、端對端的網路兒少安全系統為目標,這也是專案存在的原因之一。
- 程式碼可供稽核。 沒有隱藏邏輯,也不會被單一供應商綁定。
正式環境採用情形
Coop 由下列組織使用。
![]() | ![]() | ![]() |
|---|
正在使用 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 使用協助或希望取得聯繫,隨時歡迎採取下列方式。
- 與社群發起 discussion
- 加入 Discord 伺服器,與貢獻者、採用者及其他社群成員交流
使用者指南
用簡明的方式保護使用者免受傷害。
Coop 是 ROOST 推出的開放原始碼審查與內容治理工具,為網路安全提供完整解決方案。
-
自動處置:可自訂的多條件規則,依 signals 評估每個提交的 Item,並根據您的政策自動採取行動,或將檢舉送到人工審查 Queue
-
審查主控台:可設定的人工審查 Queue,讓內容審查員快速作成複雜且依政策判斷的治理決策,並內建額外脈絡與身心健康功能
-
整合與豐富內容處理:支援審查及比對文字內容與討論串、圖片與影片媒體、帳號資料及自訂內容類型。可使用內建 signals,以及 Google、OpenAI、Zentropi 與 NCMEC 等外部服務
-
指標與報告:提供資訊儀表板與詳細稽核紀錄,以利問責,並了解內容治理的成效與趨勢
-
API 整合:簡易 REST 與 webhook API,可讓平台雙向整合內容匯入、使用者檢舉、申訴、執行內容治理動作、取得其他 Item 等功能
-
其他功能。 Coop 還支援 NCMEC 通報、雜湊比對、使用者違規次數、申訴、調查、批次處置、完整的使用者與角色管理、依地點處置與單一登入等功能。由於專案採開放原始碼並支援自訂整合,也具備廣泛的調整空間。
Coop 如何運作
下列簡圖可協助理解資料如何在平台與 Coop 之間流動。
管理員入門
建議先熟悉 Coop 的繁中版基本概念。了解後,依序完成下列設定。
- 確認您有 Coop instance 的帳號與 API 金鑰
- 定義 Item Types,也就是平台上的內容與行為者類型
- 輸入詳細的平台政策
- 定義動作,並提供 callback 端點,讓 Coop 可觸發平台上的處置
Coop 設定完成後,平台可進行下列操作。
- 透過 Items API 將 Item 提交至 Coop,套用主動式規則
- 透過 Report API 提交使用者檢舉,將內容送入審查員使用的 Queue
台灣使用提醒
外部服務的「免費」方案、資料用途與使用條件可能變動。涉及內容、帳號、媒體、地理位置或兒少安全資料時,應先確認資料流向、保存期間、權限與契約條款。NCMEC 是美國制度,本指南提及其功能不代表台灣平台的通報義務或法定程序。
基本概念
以下是 Coop 的核心構成要素。了解這些概念,能協助您快速開始並建立工作流程。理解後,應能完成 Coop 設定,並開始使用各項功能。
這些概念依照建立 Coop 設定時的建議順序排列。 部分概念建立在前面的概念上,因此建議依序讀完。
Item
Item 是平台上的任何實體,可包含單項內容,例如貼文、留言、私訊、商品刊登與商品評論;內容討論串,例如留言串與群組聊天;或使用者及其個人檔案。即使一個實體內含其他 Item,仍可將其視為獨立 Item。
Item Type
Item Type 代表平台上的不同 Item 類型。例如,社群網路可能有 Profile、Post、Comment 與 Comment Thread。市集平台可能包含 Buyer、Seller、Product Listing、Product Review、Direct Message 與 Transaction 等。傳送至 Coop 的每個 Item,都必須只屬於其中一種 Item Type。
設定流程的第一步,是在 Coop 的 Settings → Item Types 中定義 Item Types。
Item Type 的類別
Item Type 是通用概念,可代表平台上的任何項目,從單獨的內容、使用者及其個人檔案,到包含多項相關內容的討論串皆可。
為了讓 Coop 針對不同 Item Type 提供更實用的功能,Item Type 分成三類。
-
Content:單項內容,例如訊息、留言、貼文、商品刊登與評論等
-
User:平台上的個別使用者。部分平台只有一種 User Item Type,也有平台會有多種。例如,市集可能把買家與賣家設為不同的 User Item Type,共乘服務也可能把駕駛與乘客設為不同類型
-
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。
username(string)profile_picture(image)bio(string)interests(Array<string>)
您可以加入所需數量的 Field,再於規則中使用。
Coop 如何唯一識別 Item 的重要說明
在 Coop 中,使用(Item ID, Item Type ID)組合唯一識別特定 Item。有些平台無法保證留言 ID 與使用者 ID 不會重複,也有平台營運者同時擁有及經營多個平台,無法保證不同平台的 Item ID 不會互相重複。
在這些情況下,需要使用(Item ID, Item Type ID)組合,才能唯一識別正確的 Item。在 API request 中,兩者表示為同層欄位 id 與 typeId。建議以下列結構傳送 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 執行的動作。常見的信任與安全範例包括 Delete、Ban、Mute 與 Send to Moderator。也可以加入非信任與安全用途的動作,例如 Promote、Add to Trending、Mark as Trustworthy 或 Approve 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 的 Settings → Actions 定義這些 Action。
Coop 傳送至 Action API 端點的 webhook payload 詳情,見處理 Actions。
Policy
Policy 是平台用來治理使用者行為的一組規則與指引。常見範例包括 Spam、Nudity、Fraud、Harassment 與 Violence。更多資訊見 Trust & Safety Professional Association。
Policy 可包含子政策。例如,Spam 政策可有 Commercial Spam、Repetitive Content、Fake Engagement 與 Scams & 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 Enforcement → Proactive 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 Console → Routing 設定。

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

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

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

Coop 也支援變體比對,以找出規避嘗試。例如,比對 hello 時,也可將 h3||0 與 helllllllloooo 視為符合。文字 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。

請至 Automated Enforcement → User Strikes 設定。資訊儀表板有四個分頁。
Policy Scores
每項 Policy 可對使用者違規分數提供不同權重。嚴重違規可能增加 3 分,輕微違規可能增加 1 分。違規分數是設定期間內所有違規的累計總和。
子 Policy 可繼承上層 Policy 的權重,也可自行覆寫。使用 Apply to sub-policies 切換繼承設定。
Strike Enabled Actions
Strike Enabled Actions 分頁列出組織定義的所有 Action,並可切換哪些 Action 執行後會增加使用者違規分數。並非所有 Action 都應計分。例如,「傳送警告」可能不計分,「移除內容」或「停權帳號」則可能需要計分。

任何應增加分數的 Action,都應開啟 Strike Enabled 欄位。系統會結合前一分頁的 Policy 權重與實際 Action,計算違規分數。
Thresholds & Settings
設定違規紀錄保存多久,以及使用者違規分數超過門檻時要採取的行動。

Strike Window 控制違規紀錄留在使用者資料上的時間。早於期間的紀錄不列入目前分數。相同數值也可從 Settings → Other → User Strike TTL 編輯。
Thresholds 是超過後會自動觸發 Action 的分數值,可依需要設定多個。例如下列設定。
- 5 分:將使用者送入人工審查
- 10 分:暫時限制發布內容
- 20 分:停權帳號
下拉選單中的 Action 來自組織已定義的 Action。
Analytics
此分頁顯示組織內使用者違規分數的分布圖,可在啟用門檻前協助調整。例如,多數活躍使用者分數介於 0 至 2,只有少數位於 8 以上時,將門檻設為 8,可能找出最嚴重的違規者,同時避免影響一般使用者。

Signals
Rule 條件實際評估的是 Signal。Signal 接收 Item 欄位並回傳分數,Rule 條件再將分數與門檻比較。詳情見 Signals。
台灣使用提醒
- 多個 Proactive Rule 可同時觸發,因此啟用前應測試 Action 組合是否會重複刪除、重複通知或產生互相衝突的結果
- 使用者違規分數與門檻屬於平台政策設計,不應只以技術預設值決定。高影響處置應保留理由、通知、人工複核與申訴路徑
- Hash Bank、Location Bank 與外部 Signals 可能涉及高度敏感資料。導入前應確認資料來源、存取權限、保存期限、跨境傳輸與誤比對處理方式
人工審查與處置
Review Console(審查主控台) 是內容審查員處理遭檢舉內容並作成治理決策的地方。
Queues

Coop 使用 Queue 組織審查 Job。內容遭到檢舉後,無論來源是平台使用者或主動式規則,都會進入 Queue,直到完成審查並作成決策。
內容審查員在任一 Queue 選擇 Start Reviewing 即可開始。Coop 會先取出最早的 Job,作成決策後自動載入下一個,直到 Queue 清空或審查員停止。
兩位內容審查員不會收到同一個 Job,可避免重複工作。
每位使用者可將 Queue 加上星號並固定在 Review Console 頂端。每個 Queue 都會顯示待處理 Job 數量,以協助安排優先順序。
建立與編輯 Queue
內容審查員管理者與管理員可以為組織建立及編輯 Queue。

建立或編輯 Queue 時,可設定 Reviewer Access,決定哪些內容審查員能存取及處理該 Queue 的 Job。Hidden Actions 則決定哪些 Action 不提供給這個 Queue 的審查員。這適合用來限制特定脈絡下可作成的決策,例如在第一輪分類 Queue 中隱藏永久停權。
路由規則決定新進 Job 要送到哪個 Queue。
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:無論裝置音量為何,預設將所有影片靜音
全組織預設值

管理員可以在 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 的流程如下。
- 設定整合:管理員加入外部服務的 API 憑證
- 在 Rules 中使用 Signal:在內容治理規則中引用 Signal
- 內容評估:提交內容時呼叫 Signal 並取得分數
- 執行 Action:分數超過門檻時,執行 Rule 所設定的 Action
Signals 函式庫包含文字分析、地點比對與第三方 API 整合。
文字分析
Coop 提供多種分析文字的 Signals。
-
精確關鍵字比對:在內容中尋找完全相同的詞語或片語
-
正規表示式比對:使用正規表示式在 Item 中尋找文字模式
-
文字變體比對:Coop 提供偵測常見文字字串變體的演算法,特別適合找出使用 leetspeak、替換字元、在字詞中加入標點符號或以其他方式規避處置的惡意行為者。例如,尋找
Hello時,也會將h3||0與helllllllloooo判定為符合
地點比對
您可以建立針對特定地點的 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。

輸入 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。流程如下。
- 貼上要執行 Action 的 Item ID,一次最多 1,000 個
- 選擇要對 Item 套用的 Actions
- 選擇要與 Actions 關聯的 Policies
- 選擇 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 會進行下列步驟。
- 為遭檢舉的 Item 建立內容治理 Job
- 評估 Routing Rules,決定 Job 應進入哪個 Queue
- 將 Job 送到該 Queue,若沒有符合的 Rule,則送到預設 Queue
- 讓該 Queue 中下一位可用的內容審查員取得 Job
若 reportedForReason.csam 為 true,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。

申訴會以 Job 形式進入 Review Console,與 Report 類似。Job 會顯示原本遭處置的 Item、引用的 Policy、使用者申訴理由,以及提交申訴時加入的其他脈絡。
維持或推翻申訴
內容審查員可以查看原始內容治理決策,並選擇下列結果。
- Uphold:原始 Action 正確,駁回申訴
- Overturn:原始 Action 不正確,接受申訴
作成決策後,Coop 會透過 Appeal Decision Callback 將結果送回平台,讓平台把結果告知使用者。
實作
實作 Appeal API 前,請先完成基本概念所述設定。您需要先在 Coop 設定 Item Types、Actions 與 Policies。
完整 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。
-
雜湊比對(HMA):Coop 透過 Hasher-Matcher-Actioner(HMA)整合,將上傳媒體與 NCMEC 的已知 CSAM 雜湊資料庫比對。雜湊符合是強而可靠的 Signal
-
新出現的 CSAM 偵測(Content Safety API):對於沒有已知雜湊的內容,Coop 整合 Google Content Safety API,分類圖片是否可能為 CSAM。高信心結果可直接送至 NCMEC Queue,也可先送至初步分類 Queue
-
標記為 CSAM 的新進 Report:平台傳送標記為 CSAM 的使用者 Report 至 Coop 時,Coop 會直接送至 NCMEC Queue,不評估一般 Routing Rules
-
人工升級:在任何審查 Job 中,具有 NCMEC 存取權的內容審查員都可從 Action 清單選擇 Enqueue to NCMEC,立即將 Job 移至 NCMEC Queue
設定方式見將內容路由至 NCMEC。
內容進入 Queue 後的處理
內容透過上述任一路徑進入 NCMEC Queue 後,Coop 會進行下列步驟。
- 透過內容 Item 的
creatorId欄位辨識相關使用者。若 Item 本身是 User Type,則直接使用該 Item - 檢查該使用者是否已有開啟中的 NCMEC Job。若有,加入新內容並更新,不建立重複 Job
- 取得平台上曾與該使用者關聯的所有媒體
- 建立單一彙整 NCMEC 審查 Job,包含使用者及其所有媒體
- 將 Job 送至已設定的 NCMEC Queue
以使用者為中心的彙整方式,會讓同一位使用者即使上傳多項 CSAM,也只建立一個 NCMEC 審查 Job,並向 NCMEC 提交一份較具行動價值的 CyberTip,不會為每項內容分別提交。
資料範圍提醒 「取得所有媒體」可能大幅擴張審查與跨境傳輸的資料範圍。實際採用時應確認是否具有適當權限與必要性、哪些媒體可納入、失敗或誤標時如何停止,以及未送出資料如何隔離與刪除。
審查 NCMEC Job
NCMEC Job 使用者介面與標準審查 Job 不同,設計重點是使用者及與其相關的全部媒體。

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

Overview 資訊儀表板提供內容治理活動的高階概況。所有指標都可在可設定期間內,依小時或每日篩選。Overview 顯示下列內容。
- 已採取的 Action 總數:所選期間內所有內容治理 Decision 的數量
- 待審查 Job:目前在 Queue 中等待內容審查員處理的 Job 數量
- 自動與人工 Action:由 Proactive Rules 與人工審查員作成之 Decision 的百分比分布
- 最常見的 Policy 違規:哪些 Policy 產生最多 Action
- 每位內容審查員的 Decision:工作在審查團隊中的分布情形
- 每條 Rule 的 Action:哪些 Rule 最常觸發,只有啟用 Rule 時才顯示
- 各 Policy 的違規:一段時間內,各 Policy 之下採取的 Action 數量
Recent Decisions

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

可下載完整紀錄,也可依 Decision、Policy、Queue、內容審查員與日期範圍篩選後下載。特別適合下列用途。
- 透明度報告:匯出 Decision,納入提供給主管機關或監督單位的報告
- 品質保證與稽核:抽樣個別內容審查員或自動 Rule 作成的 Decision,檢查一致性與準確度
- 推翻工作流程:從紀錄的 Decision 回到原始 Job,並依需要執行反向 Action
也可另外下載只包含內容審查員曾經 skip 的 Job。這有助於找出可能長期難以判定的內容。
台灣使用提醒
- Action 數量與處理速度無法單獨代表治理品質。建議同時觀察誤判、推翻率、申訴結果、等待時間、審查員負荷與不同 Policy 的差異
Decisions per moderator適合分析工作分配,不宜脫離案件難度與身心健康因素,直接當成個人績效排名- 匯出資料可能含帳號、內容、理由與審查員資訊。提供給外部單位前應確認目的、欄位最小化、去識別、存取權與保存期限
管理與設定
管理員從 Settings 選單管理組織設定,以及 Item、Action、Policy、使用者存取與整合的個別設定。
Settings
在 Settings 設定組織資料與全組織行為,包括單一登入、申訴、Review Console、審查員身心健康等功能。許多可切換功能預設關閉,請依平台與團隊需要選擇啟用。
Organization
Coop 組織的識別與聯絡資訊。

On-Call Alert Email 需要在 Coop 部署中整合電子郵件服務。Coop 支援電子郵件服務整合,但不會預先設定服務。
Single Sign-on
啟用以 SAML 為基礎的 SSO,讓使用者透過組織的身分提供者驗證,不使用電子郵件與密碼。Coop 支援任何 SAML 2.0 身分提供者。以 Okta 設定的範例,見部署指南的 Single Sign-on。

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

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

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

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

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

建立 Item Type 時,請定義 Schema,指定要包含及顯示給內容審查員的 Fields。這些 Fields 也可用於 Rule 邏輯,連接 Signals 以進行路由或自動化。
Actions
Action 代表 Proactive Rule 或內容審查員 Decision 可對 Item 執行的任何動作。詳情見基本概念的 Actions。

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

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

從 Coop 使用者介面加入的 Policy,會直接顯示在 Review Console 的 Job 檢視,供內容審查員查閱。
使用者管理
Coop 使用角色式存取控制,確保只有適當的人員能存取相應資料。

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

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

變更會立即套用至所有具有該角色的使用者。只有具有 Manage Roles 權限者能存取角色編輯器,Admin 預設具有此權限。
API Keys
Coop 使用 API keys 驗證平台與 Coop 之間的 request。
Coop API key
平台傳送 request 至 Coop 時,應在每次 request 中以 HTTP header 加入組織 API key。您可在 Settings → API Keys 查找或輪替金鑰。
X-API-KEY: <<apiKey>>
Content-Type: application/json
若要確認 Action 端點收到的 request 確實由 Coop 傳送,請使用 Settings → API 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 使用 bash 或 zsh。Prerequisites、詳細設定、疑難排解等資訊,見本機開發。系統元件與資料流見架構。
執行 Coop 的步驟如下。
-
若尚未完成,使用
gitclone repository 並進入coop資料夾git clone https://github.com/roostorg/coop.git && cd coop -
確認已安裝 prerequisites,包括
nvm、docker與正確版本的 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與目前文件為準。 -
從 root folder 與各 sub-package 使用
npm安裝 dependencies# coop/ npm install (cd db && npm install) (cd server && npm install) (cd client && npm install) -
在
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 -
啟動所有 backing services,包括 databases 與 Queues。Ports 與詳細資訊見本機開發的 Docker services
# coop/ npm run up等待 PostgreSQL、ClickHouse、ScyllaDB 與 Redis 進入 healthy 狀態後再繼續。可使用
docker ps查看進度。 -
建立 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 -
使用
server/folder 中的 script 複製 static asset files# coop/ cd server # coop/server/ npm run copy-assets -
從
server/folder 使用create-orgscript,提供適當資訊,建立 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,請立即複製並保存在安全位置。
-
最後啟動 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 24、nvm 與 npm
- Docker 與 Docker 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。
| Service | Port | 說明 |
|---|---|---|
| PostgreSQL | 5432 | Primary database |
| ClickHouse | 8123、9000 | Analytics warehouse |
| ScyllaDB | 9042 | Item submission history |
| Redis | 6379 | Caching 與 job queues |
| Jaeger | 16686 | Tracing UI |
| OTEL Collector | 4317 | Telemetry 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:clean與db: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 中,分別執行 server 與 client 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。
存取位置
| Service | URL |
|---|---|
| Client | http://localhost:3000 |
| API Server | http://localhost:8080 |
| GraphQL | http://localhost:8080/graphql |
| Jaeger UI | http://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_graphql | docker 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.tsserver/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 keynpm 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_USERNAME 與 CLICKHOUSE_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
| Layer | Technologies |
|---|---|
| Frontend | React、TypeScript、Ant Design、TailwindCSS、Apollo Client |
| Backend | Node.js、Express、Apollo Server、TypeScript |
| Databases | PostgreSQL、Scylla(5.2)、ClickHouse、Redis |
| Messaging | BullMQ(Redis) |
| ORM | Sequelize、Kysely |
| Auth | Passport.js、express-session、SAML(SSO) |
| Observability | OpenTelemetry |
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 包括 LIVE、DRAFT、BACKGROUND、EXPIRED。
- Code:
/server/models/rules/RuleModel.ts - Storage tables
manual_review_tool.routing_rulesmanual_review_tool.routing_rules_to_item_typesmanual_review_tool.routing_rules_historymanual_review_tool.appeal_routing_rulesmanual_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.rulespublic.rules_and_actionspublic.rules_and_item_typespublic.rules_and_policiespublic.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
IGNORECUSTOM_ACTIONSUBMIT_NCMEC_REPORTACCEPT_APPEALREJECT_APPEALTRANSFORM_JOB_AND_RECREATE_IN_QUEUEAUTOMATIC_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 webhookENQUEUE_TO_MRT:傳送至 Review ConsoleENQUEUE_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 參考
| Property | Type | 是否固定存在 | 說明 |
|---|---|---|---|
item | Item | 一律存在 | 應執行此 Action 的 Item |
action | Action | 一律存在 | 正在觸發的 Action 資訊 |
policies | Array<Policy> | 一律存在 | 與此 Action 關聯的 Policies。多項 Rules 觸發同一 Action 時可能包含多筆 |
rules | Array<Rule> | 不一定 | 觸發此 Action 的 Rules。由人工審查或批次處置觸發時為空 |
custom | Object | 不一定 | 在 Action form 的「Body」中設定的自訂參數 |
actorEmail | String | 不一定 | 執行 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_keysjobs:scheduled job trackingmanual_review_tool:manual review Queues、Decisions、Routing Rules、commentsncmec_reporting:兒少安全 NCMEC reportsreporting_rules:user/content reporting Rulessignal_service:Signal configurationuser_management_service:user managementusers_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 tableitem_submission_by_item_id:以 Item ID lookupitem_submission_by_thread_and_time:以 thread 與 time range lookupitem_submission_by_creator:以 creator lookup
ClickHouse
作為 analytics、aggregations 與 audit trails 的 OLAP storage。
Databases 與主要 tables 如下。
analytics:RULE_EXECUTIONS、ACTION_EXECUTIONS、CONTENT_API_REQUESTS、ITEM_MODEL_SCORES_LOG- Action executions:
ACTION_STATISTICS_SERVICE下的BY_ACTION、BY_RULE、BY_POLICY、ACTIONED_SUBMISSION_COUNTSMANUAL_REVIEW_TOOL下的ROUTING_RULE_EXECUTIONS
- Reporting 與 Appeal statistics:
REPORTING_SERVICE下的REPORTS、APPEALS、REPORTING_RULE_EXECUTIONS - User-level metrics:
USER_STATISTICS_SERVICE下的LIFETIME_ACTION_STATS、SUBMISSION_STATS、USER_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 RulesANALYST:可查看 insightsMODERATOR_MANAGER:管理 MRT QueuesMODERATOR:審查被指派 QueuesCHILD_SAFETY_MODERATOR:可存取 NCMEC dataEXTERNAL_MODERATOR:只有 MRT view access
Permissions 如下。
MANAGE_ORG:ADMINMUTATE_LIVE_RULES:ADMIN、RULES_MANAGERVIEW_MRT:所有 moderator rolesEDIT_MRT_QUEUES:ADMIN、MODERATOR_MANAGERVIEW_CHILD_SAFETY_DATA:ADMIN、MODERATOR_MANAGER、CHILD_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。
- Middleware 取出
x-api-keyheader - 透過 database 中的 SHA-256 hash lookup 驗證 key
- Key 有效時,在 request 設定
orgId,供 downstream handlers 使用 - 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。
- User 透過 GraphQL login mutation 提交 credentials
- Passport 的
GraphQLLocalStrategy驗證 email/password - 透過 bcrypt comparison 驗證 password
- 驗證成功後,使用
passport.serializeUser()將 user serialized 至 session - 透過
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 分別設定。
- User 前往
/saml/login/{orgId} - Passport 的
MultiSamlStrategy取得該 organization 的 SAML settings - User redirected 至設定的 SAML provider
- Provider 完成 authentication,並將 assertion POST 至 callback URL
- 系統從 SAML assertion 取出 user email
- Lookup user record 並建立 session
各 organization 的 configuration 儲存於 org_settings table。
saml_enabled:Boolean flagsso_url:SAML entry point URLcert:供驗證使用的 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 的 Settings → API 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 顯示於 Settings → API Keys 的「Webhook Signature Verification Key」。需要時可在該處產生新 key。輪替後,請使用新的 public key 更新驗證邏輯。
使用 signature header 驗證 request
Coop 會將 signature 放在 Coop-Signature header,部分 client 會將名稱顯示為 coop-signature。驗證傳入 HTTP request 的步驟如下。
- 使用 SHA-256 對 request body 進行 hash。輸入必須是未經處理的原始 request body bytes。
- 對
Coop-Signatureheader 的值進行 Base64 decode,取得原始 binary signature。 - 使用 public key 驗證 signature。Coop 使用 RSASSA-PKCS1-v1_5 與 SHA-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-signature 或 Coop-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 內建 ClickHouse 與 PostgreSQL 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);
執行流程
- Service 呼叫
logger.logRuleExecutions(data) - Logger 呼叫
analytics.bulkWrite('RULE_EXECUTIONS', data) - 使用 ClickHouse 時,透過 HTTP 進行分批 JSONEachRow inserts,預設每批 500 rows
- 使用 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 logsACTION_EXECUTIONS:內容治理 Action logsITEM_MODEL_SCORES_LOG:ML model prediction logsCONTENT_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.createDataWarehouse、createKyselyDialect 與 createAnalyticsAdapter,讓它們建立新 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
| Service | Type | 用途 |
|---|---|---|
DataWarehouse | IDataWarehouse | Raw SQL、transactions |
DataWarehouseDialect | IDataWarehouseDialect | Type-safe queries |
DataWarehouseAnalytics | IDataWarehouseAnalytics | Bulk 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 - ClickHouse:
server/plugins/warehouse與server/plugins/analyticsadapters - PostgreSQL migrations:
db/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
- Postgres、Redis、ScyllaDB、ClickHouse
- 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 詳細資訊
| Image | Dockerfile | Build target | Base |
|---|---|---|---|
coop-server | Dockerfile | build_server | node:24-bullseye-slim + dumb-init |
coop-worker | Dockerfile | build_worker_runner | node:24-bullseye-slim + dumb-init |
coop-client | client/Dockerfile | serve | nginx:1.27-bookworm |
coop-migrations | db/Dockerfile | final stage | node: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-pg、scylla 與 clickhouse。Connection settings 來自 environment variables,見 db/.env.example。任何 command 啟動時都會讀取 database configs,因此必須提供這些設定。
--env 控制的範圍
--env 可為 staging 或 prod,只影響 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:clean與db:drop是 destructive commands,會拒絕--env prod,作為安全保護
部署提醒
- 正式環境不要使用
latesttag,應固定經驗證的 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.example、server/.env.example 與 client/.env.example 的範例環境檔為起點,建立符合自身部署環境的設定。
Production 必要設定
Production 部署至少需要提供下列項目。
- API server Postgres instance 與 database migrator 的 database connection
- Queue 與 background processing 使用的 Redis connection
- Item submission history 使用的 Scylla connection
SESSION_SECRET與GRAPHQL_OPAQUE_SCALAR_SECRET等 session 及 token secretsUI_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_ADAPTER和ANALYTICS_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 後,請依序確認下列事項。
- 移除或妥善保護範例組織,以及所有使用預設密碼建立的使用者。
- 確認 production hostname 與 email 設定正確。
- 確認 warehouse 及 analytics adapters 與實際部署的 backing services 一致。
- 除非確定要傳送正式 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
設定步驟如下。
-
在 Okta 建立 custom SAML application,並使用下列設定。
設定 值 Single sign-on URL 組織的 callback URL,例如 https://your-coop-instance.com/login/saml/12345/callback。可在 Coop 的 Settings → SSO 找到。Audience URI (SP Entity ID) Coop instance 的 base URL,例如 https://your-coop-instance.com。emailattribute(位於 Attribute Statements)email。實際值取決於 identity provider 的 attribute mappings,例如 Google SSO 可能使用「Primary Email」。 -
在 Feedback tab 勾選 I’m a software vendor. I’d like to integrate my app with Okta。
-
前往 app settings 的 Sign On tab,在 SAML Signing Certificates → SHA-2 下點選 Actions → View IdP metadata。
-
複製 XML file 的內容。在 Coop 前往 Settings → SSO,將 XML 貼入 Identity Provider Metadata field。
-
在同一頁的 Attributes section 輸入
email。 -
在 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.1tag 的 infrastructure code 僅供歷史參考,不應未經審查就直接部署
API 參考
本節介紹 Coop API,包括平台用來與 Coop 整合的 REST API endpoints。
所有 endpoints 都要求每次 request 透過 HTTP header 傳送 API key。
X-API-KEY: <<apiKey>>
Content-Type: application/json
您可以從 Coop 使用者介面的 Settings → API Keys 查找或輪替 API key。驗證 Coop 加在外送 webhook request 上的 signature,見 API Keys 與 Authentication。
| Endpoint | 說明 |
|---|---|
POST /api/v1/items/async/ | Items,傳送內容供 Rule 評估 |
POST /api/v1/report | Report,提交使用者檢舉 |
POST /api/v1/report/appeal | Appeal,提交使用者申訴 |
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
| Field | Type | 必要性 | 說明 |
|---|---|---|---|
items | Array | 必要 | 要提交的一個或多個 Items |
items[].id | String | 必要 | 平台為 Item 使用的唯一識別碼 |
items[].typeId | String | 必要 | 從資訊儀表板設定之 Item 的 Coop Item Type ID |
items[].data | Object | 必要 | Item payload。Fields 必須符合 Item Type 定義的 schema |
items[].data.images | Array | 選填 | URL strings 的 Array,會觸發自動 HMA 圖片雜湊 |
items[].typeVersion | String | 選填 | 供 schema versioning 使用的 version string |
items[].typeSchemaVariant | String | 選填 | Schema variant,有效值為 "original" 或 "partial" |
data Fields 格式
data 的形狀必須符合 Item Type schema。常見 Field types 如下。
| Field type | 格式 |
|---|---|
| String | 一般 string value |
| Number | JSON number |
| Boolean | true 或 false |
| Image/Audio/Video | 指向媒體的 URL string |
| Geohash | Base-32 geohash string |
| Datetime | ISO 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 |
401 或 403 | 驗證失敗 |
完整 error response 格式見 Errors。
自動圖片雜湊
若 Item 的 data Object 包含由 URL strings Array 構成的 images Field,Coop 會自動進行下列步驟。
- 從提供的 URLs 取得圖片內容
- 為每張圖片計算感知雜湊
- 與組織已設定的所有 HMA Matching Banks比對
- 將產生的 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
| Field | Type | 必要性 | 說明 |
|---|---|---|---|
reporter | Reporter | 必要 | 提交 Report 的使用者 |
reportedAt | Datetime | 必要 | Item 遭檢舉時間的 ISO 8601 timestamp |
reportedItem | ReportedItem | 必要 | 遭檢舉的 Item |
reportedItem.data.images | Array | 選填 | URL strings 的 Array,會觸發自動 HMA 圖片雜湊 |
reportedForReason | ReportedForReason | 選填 | Item 遭檢舉的原因 |
reportedItemThread | Array<ReportedItem> | 選填 | 同一 Thread 中的其他 Items,例如私訊 Thread 中的前後訊息。Coop 用來向內容審查員顯示完整脈絡 |
reportedItemsInThread | Array<ItemIdentifier> | 選填 | reportedItemThread 中明確遭檢舉的 Items,會在審查使用者介面標記 |
additionalItems | Array<ReportedItem> | 選填 | 與 Report 一起顯示的補充內容,例如作者近期貼文 |
Reporter schema
| Field | Type | 必要性 | 說明 |
|---|---|---|---|
kind | String | 必要 | 檢舉實體類型,目前只支援 "user" |
id | String | 必要 | 平台對檢舉使用者使用的唯一識別碼 |
typeId | String | 必要 | 從 Item Types 資訊儀表板設定的檢舉使用者 Item Type ID |
ReportedItem schema
| Field | Type | 必要性 | 說明 |
|---|---|---|---|
id | String | 必要 | 平台對遭檢舉 Item 使用的唯一識別碼 |
typeId | String | 必要 | 遭檢舉 Item 的 Item Type ID |
data | JSON | 必要 | Item payload,必須符合 Item Type 定義的 schema |
reportedItemThread 使用與 ReportedItem 相同的 schema,但不嚴格強制必要 Fields,以支援追溯取得資料。Thread Items 應包含 datetime Field,確保正確依時間排序。
ItemIdentifier schema
| Field | Type | 必要性 | 說明 |
|---|---|---|---|
id | String | 必要 | 平台對 Item 使用的唯一識別碼 |
typeId | String | 必要 | Item 的 Item Type ID |
ReportedForReason schema
| Field | Type | 必要性 | 說明 |
|---|---|---|---|
policyId | String | 選填 | 若檢舉者選擇的原因已對應 Policy,填入違反 Policy 的 ID |
reason | String | 選填 | 檢舉者說明提交原因的自由格式文字 |
csam | Boolean | 選填 | 設為 true 時,Coop 將 Job 直接送至 NCMEC Queue,不進入預設審查 Queue |
Response
成功時回傳由 Coop 指派的唯一 Report ID。
{ "reportId": "report-uuid" }
| Field | Type | 說明 |
|---|---|---|
reportId | String | Coop 指派給 Report 的唯一 ID |
HTTP statuses 如下。
| Status | 意義 |
|---|---|
201 Created | 已收到 Report,回傳 reportId |
400 Bad Request | 驗證失敗,見 Errors |
401 或 403 | 驗證失敗 |
完整 error response 格式見 Errors。
資料與路由提醒
reportedItemThread與additionalItems會擴大內容審查員可見資料。只應提供判斷必要的前後文,不要預設傳送整個私訊歷程或作者全部近期內容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
| Field | Type | 必要性 | 說明 |
|---|---|---|---|
appealId | String | 必要 | 平台內部的 Appeal submission ID。內容審查員處理申訴時,會將此值傳回平台 |
appealedBy | ItemIdentifier | 必要 | 提出申訴的使用者,包括平台內部 user ID 與該使用者的 Coop Item Type ID |
appealedAt | Datetime | 必要 | 提交申訴時間的 ISO 8601 timestamp |
actionedItem | Item | 必要 | 原本遭執行 Action 的 Item |
actionsTaken | Array<String> | 必要 | 已執行並送至 Action callback 的 Action Coop IDs |
appealReason | String | 選填 | 使用者說明申訴原因的自由格式文字 |
violatingPolicies | Array<Policy> | 選填 | 最初內容治理 Action 執行時,從 Action webhook 收到的 Policies |
additionalItems | Array<Item> | 選填 | 與申訴一起顯示、提供脈絡的補充內容 |
Response
| Status | 意義 |
|---|---|
204 No Content | 已成功收到 Appeal |
400 Bad Request | 驗證失敗,見 Errors |
401 或 403 | 驗證失敗 |
完整 error response 格式見 Errors。
資料與流程提醒
appealReason與additionalItems應限制為複核必要內容,不應把整個帳號或無關對話歷程一併提交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
| Field | Type | 說明 |
|---|---|---|
policies | Array | 組織的全部 Policies |
policies[].id | String | Coop 為 Policy 建立的唯一且不可變 ID |
policies[].name | String | 使用者為 Policy 指定的顯示名稱 |
policies[].parentId | String 或 null | 上層 Policy 的 ID,頂層 Policy 則為 null |
注意事項
- 使用
parentId重建完整 Policy tree。parentId為null代表頂層 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 參考
| Field | Type | 是否固定存在 | 說明 |
|---|---|---|---|
item | Item | 一律存在 | 應執行此 Action 的 Item |
action | Action | 一律存在 | 正在觸發的 Action |
policies | Array<Policy> | 一律存在 | 與此 Action 相關的 Policies。多個 Rules 觸發相同 Action 時,可能包含多筆 |
rules | Array<Rule> | 不一定 | 觸發此 Action 的 Rules。由人工審查或批次處置觸發時為空 |
custom | Object | 不一定 | 在 Action 表單 Body 中設定的自訂參數。對遭檢舉 Item 的 Review Console Decision,Coop 也會將 reason 與 reportHistory 合併至此 Object,見下方 Custom Object |
actorEmail | String | 不一定 | 執行 Action 之 Coop 使用者的 email。自動 Rule 觸發時省略 |
actorNote | String | 不一定 | 內容審查員執行 Action 時加入的 note,未填寫時省略 |
creator | ItemIdentifier | 不一定 | Action 所涉及的使用者。USER Item 使用目標本身,CONTENT Item 使用內容作者。無法解析 creator 時省略,例如只有 content ID 且沒有已知 submission |
decisionReason | String | 不一定 | 內容審查員從 Review Console 或 Submit Decision API 提供的 Decision reason。Review Console Decision 有填寫 reason 時存在,其他情況省略 |
userStrikeCount | Number | 不一定 | 此 Action 後使用者的累計違規分數,包括既有分數及本次事件加入的分數。無法解析目標使用者時省略 |
Item schema
| Field | Type | 說明 |
|---|---|---|
id | String | 平台為 Item 使用的唯一識別碼 |
typeId | String | Item Type ID |
typeName | String | Item Type 顯示名稱 |
Policy schema
| Field | Type | 說明 |
|---|---|---|
id | String | Coop 唯一 Policy ID |
name | String | Policy 名稱 |
penalty | String | Penalty level,可為 NONE、LOW、MEDIUM、HIGH 或 SEVERE |
Rule schema
| Field | Type | 說明 |
|---|---|---|
id | String | Coop 唯一 Rule ID |
name | String | Rule 名稱 |
Custom Object
除了在 Body 中設定的參數,對遭檢舉 Item 作成 Review Console Decision 時,Coop 也會將下列資訊合併至 custom。Decision reason 同時出現在頂層 decisionReason Field,因此會在兩處出現。
| Field | Type | 說明 |
|---|---|---|
reason | String | 內容審查員的 Decision reason,與頂層 decisionReason 相同 |
reportHistory | Array<Report> | 對 Item 提出的 Reports,每筆格式為 { reason, reporter } |
User Strikes
使用者累計違規分數超過設定門檻時,Coop 會使用相同 callback 機制,執行與該門檻關聯的 Action。與 Rule 觸發 callback 的差異如下。
policies一律為空 Array,因為門檻依累計分數觸發,不代表此次 request 的特定 Policy 違規rules一律為空 Array,沒有 Rule 直接觸發 callbackactorEmail與actorNote一律不存在,因為沒有人工執行者
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 參考
| Field | Type | 是否固定存在 | 說明 |
|---|---|---|---|
appealId | String | 一律存在 | 平台內部 Appeal ID,與透過 Appeal API 傳送的值相同 |
item | Item | 一律存在 | 原本遭執行內容治理 Action 的 Item |
appealedBy | ItemIdentifier | 一律存在 | 提交 Appeal 的使用者 |
appealDecision | String | 一律存在 | ACCEPT 代表原始 Action 不正確並接受 Appeal,REJECT 代表維持原始 Action |
custom | Object | 不一定 | Appeal Configuration Form 的 Body 中設定的自訂參數 |
完整 Appeal submission 流程見申訴。
安全、隱私與可靠性提醒
- 驗證簽章時應使用原始 request body,並以 Coop authentication 文件與實作指定的演算法為準。目前 signature 格式沒有 timestamp 或 nonce,重試與 replay 的影響需由冪等 handler 與事件紀錄控制
actorEmail、decisionReason、actorNote與reportHistory可能包含個人資料、敏感內容或內部判斷,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。
| Property | Type | 說明 |
|---|---|---|
id | String | 平台為 Item 使用的唯一識別碼 |
typeId | String | 對應 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 正確。
| Property | Type | 必要性 | 說明 |
|---|---|---|---|
id | String | 必要 | Coop 在 request body 提供的 id |
typeId | String | 必要 | Coop 在 request body 提供的 typeId |
data | Object | 必要 | 與 Items API 傳送內容相同的形狀。可為空 Object {},但 key 必須存在 |
typeVersion | String | 選填 | 指定目標 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 會被無聲捨棄。
另一種可接受的格式,是將 typeId、typeVersion 與 typeSchemaVariant 放在 type Object 中,分別使用 id、version 與 schemaVariant。新整合建議使用上述扁平格式,巢狀格式只為了與 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 statusPartialItemsInvalidResponseError:body 可解析為 JSON,但不符合上述 schema,常見原因是缺少data、頂層itemskey,或id/typeId不是 string;也可能完全無法解析為 JSON。Body 解析失敗時,Coop server logs 會包含 response bytes 的短 prefix。最常見原因是重複寫入 response,例如 middleware 在 payload 前先寫入 sentinel,產生null{"items":[...]}
Request 看似成功但 Item 仍未顯示時,請確認每個回傳 Item 的 (id, typeId) 與 Coop request 完全相同。不相符的資料會被無聲捨棄。透過 tunnel 測試時,例如 localtunnel 或 ngrok,請確認 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 OK | Request 成功,response body 包含資料 |
202 Accepted | 已收到 Item submission,並排入非同步處理 Queue |
204 No Content | Request 成功,沒有 response body,例如 Report 與 Appeal submissions |
400 Bad Request | Request 無效,包括 JSON 格式錯誤、缺少必要 Fields 或 schema 不相符,例如 Report 引用不存在的 Item。詳情見 error body |
401 或 403 | 驗證失敗,API key 缺漏、無效或過期 |
429 Too Many Requests | 超過 rate limit |
500 或 503 | Server 內部錯誤,通常是暫時性問題,可安全重試 |
502 或 504 | Gateway 或 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 | 說明 |
|---|---|
status | HTTP status code |
type | Error type identifiers 的 Array |
title | 簡短、供人閱讀的摘要 |
detail | 補充 error 脈絡,若有 |
pointer | 指向造成 error 之 Field 的 JSON pointer,若適用 |
requestId | 供 tracing 使用的 correlation ID |
整合提醒
- 對
500、502、503、504與429重試時,應使用有上限的 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 | 免費4 | OpenAI API key |
| Zentropi CoPE | 免費,另有付費選項5 | Zentropi API key |
Model cards
每項整合都有 model card,以一致且可比較的方式說明運作方式。Model card 是描述機器學習模型預定用途、行為與限制的短文件,可視為 AI 分類器的營養標示。
各整合的 model card 說明下列欄位。
| 欄位 | 說明 |
|---|---|
| Purpose | 模型設計要偵測或分類的內容 |
| Input | 模型接受的內容類型,例如圖片、文字或網址 |
| Output | 模型回應的格式與意義 |
| Limitations | 已知缺漏、失敗模式或難以處理的內容類型 |
| Requirements | 使用整合所需的存取、核准或設定 |
| Best practices | 取得可靠結果的建議 |
自動分類器都可能出錯。Model card 可協助判斷 Signal 在何種情況下可供參考、哪些結果需要人工審查,以及如何合理設定 Rule。
-
希望保護平台免受濫用的產業與公民社會第三方,可申請 Content Safety API 存取權。申請時請提及使用 Coop 審查工具。申請需經核准,並接受 Google 條款及細則。 ↩
-
使用自有 Hash Bank 不需額外憑證或授權,例如組織自行保存的已知違規內容集合。使用各第三方雜湊則需取得該組織授權。例如,NCMEC 需提供 Hash Sharing API 憑證,Tech Against Terrorism 也會管理其 Hash Bank 存取權。 ↩
-
需要完成 NCMEC ESP registration,並經核准取得 CyberTip API 憑證。 ↩
-
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。已知內容即使經過輕微修改,仍可可靠比對。
要求
- Coop 伺服器可存取、正在執行的 HMA instance
- 要使用之第三方 Hash Bank 的 API 憑證。例如,NCMEC 提供 Hash Sharing API 憑證,Tech Against Terrorism 則管理其 Hash Bank 存取權
- 自有 Hash Bank,選填,例如組織自行保存的已知違規內容集合
- 在 Coop 的 Settings → Integrations 設定 HMA 服務網址
連線後,HMA Signals 會顯示在 Coop Signal 函式庫,可供 Routing Rules 與 Proactive Rules 使用。
管理 Hash Banks
Hash Bank 是已知有害媒體指紋的集合,可供 Rule 引用。您可以透過 Coop 使用者介面建立及管理 Bank,也可從 NCMEC 等外部來源同步。

透過 Coop 建立 Bank
建議從 Coop 的 Settings → Matching 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。

無論 Bank 從何處建立,都可以使用 HMA 使用者介面手動加入內容,供本機測試。
NCMEC Hash Sharing
若要讓 Coop 與 NCMEC 的已知 CSAM 雜湊資料庫比對,需要 NCMEC Hash Sharing API 憑證。
在 HMA 建立以 NCMEC exchange 為來源的 Bank。HMA 會依背景擷取排程開始同步雜湊,預設每五分鐘一次。同步完成後,NCMEC 來源 Bank 會顯示在 Coop Matching Banks。
在 Rules 中使用 HMA Signals
HMA 連線並設定 Hash Banks 後,圖片雜湊 Signal 可同時用於 Routing Rules 與 Proactive Rules。
-
若要路由來自使用者 Report 的內容,請建立含雜湊比對邏輯的路由規則,並送至所需 Queue。若是 NCMEC 符合項目,應送至已設定的 NCMEC Queue

-
對透過 Items API 提交的內容,若希望 Coop 在沒有使用者 Report 時主動計算雜湊並標記符合項目,請建立含圖片雜湊條件及「Enqueue to NCMEC」Action 的主動式規則
另請參閱
- 自動處置與路由,了解如何建立 Rule
- NCMEC CyberTipline,了解 NCMEC 整合設定
台灣使用提醒
- 雜湊符合只能證明計算結果與資料庫項目相符,實際意義仍取決於資料庫品質、來源授權、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 通報內容前,需要具備下列條件。
- 完成 NCMEC Electronic Service Provider(ESP)registration 並取得核准
- CyberTipline API 憑證,包括 NCMEC 提供的 username 與 password,用來向 CyberTipline API提交 Report
- 具有
creatorIdField 的 User Item Type。NCMEC Job 以使用者為中心,不以個別內容為中心。Coop 透過內容 Item 的creatorIdField 取得使用者。該 Field 是參照 User Item Type 的RELATED_ITEM,接著彙整與該使用者相關的所有媒體,建立單一 NCMEC 審查 Job - 專用 NCMEC 人工審查 Queue,供 Coop 路由 Job。Queue 中的 Decision 會送出真實 CyberTip 或進入 NCMEC sandbox,由 Coop server 的
NCMEC_ENVenvironment variable 控制,詳情見測試與正式提交 - Additional Info endpoint,選填但強烈建議。Coop 在提交 CyberTip 前呼叫此 webhook,取得電子郵件、screen name、IP capture events 與每項媒體的詳細資訊。未設定時,只會使用 user ID 與 Item data 中的基本資訊提交
- Preservation endpoint,選填。CyberTip 成功提交後,Coop 呼叫此 webhook,讓平台依 NCMEC 要求保存相關使用者資料。Coop 沒有內建資料保存功能,只會在設定後呼叫所提供的 endpoint
- 在 Coop 的 Settings → NCMEC 完成上述 NCMEC 組織設定,詳情見下方 NCMEC settings
雜湊比對(HMA)
若要透過雜湊比對自動偵測已知 CSAM,需要 NCMEC Hash Sharing API 憑證。請在 HMA curator 使用者介面設定,或在 HMA service 使用 TX_NCMEC_CREDENTIALS environment variable。
詳情見 Hasher-Matcher-Actioner(HMA)整合。
NCMEC settings
請從 Settings → NCMEC Settings 設定 NCMEC 通報。

| 設定 | 說明 |
|---|---|
| Username | NCMEC CyberTipline API username |
| Password | NCMEC CyberTipline API password |
| Company Report Name | 組織在 NCMEC Report 中使用的名稱,也會在每份 CyberTip 中作為遭通報使用者的 ESP service name |
| Legal URL | 服務條款或法律政策網址,例如 https://yourcompany.com/terms |
| Contact Email | CyberTip 通報人的電子郵件。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 Endpoint | CyberTip 成功提交後,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 依序進行下列步驟。
-
取得補充資訊:呼叫 Additional Info endpoint,取得使用者電子郵件、screen name、IP capture events 與每項媒體的詳細資訊
-
建立 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:使用者在平台內部的 IDespService:組織名稱,來自companyTemplatescreenName:使用者 username,來自 Additional Info endpointdisplayName:使用者 display name,若有email:已知電子郵件地址,來自 Additional Info endpointipCaptureEvent:與使用者相關的 IP addresses,例如登入與上傳事件,來自 Additional Info endpoint。提供 IP 資料會顯著改善 NCMEC 辨識及定位嫌疑人的能力。若 User Item 的 Item Type 將ipAddress對應至 schema Field role,該 IP 也會以Unknownevent 及 Report incident time 加入
- Victim:若能辨識兒少被害人,例如來自 messaging 脈絡,Coop 會加入其
espIdentifier、screenName、displayName與ipCaptureEvent,協助 NCMEC 定位並提供協助
-
提交 Report:Coop 以 POST 將 Report XML 送至 NCMEC CyberTipline API,並取得
reportId -
上傳媒體:Coop 針對每項媒體,從網址下載檔案並上傳至 NCMEC,附上完整檔案 metadata
- Industry classification(A1/A2/B1/B2)
- File annotations,審查員選擇的 labels
- 與上傳相關的 IP capture events。若 Media Item 的 Item Type 將
ipAddress對應至 schema Field role,該 IP 也會以Uploadevent 及媒體createdAt加入 - 內容是否曾在平台公開,
publiclyAvailable - ESP 是否查看檔案與 EXIF data,
fileViewedByEsp: true、exifViewedByEsp: true - Additional Info endpoint 提供的 file hash,若有
-
上傳補充檔案:Additional Info endpoint 回傳的其他檔案,例如螢幕截圖與佐證資料,會以 supplemental reported files 上傳至 NCMEC
-
上傳 message threads:若使用者在 messaging 脈絡遭到 Report,Coop 會為每個 conversation thread 建立 CSV 並上傳至 NCMEC
-
完成 Report:呼叫 NCMEC
/finishendpoint 完成提交 -
保存 Report:將完成的 Report,包括 Report ID、XML 與所有媒體詳細資訊,保存至 Coop database
-
傳送 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_ENV 與 Settings → NCMEC 設定的 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
| Field | Type | 說明 |
|---|---|---|
users | Array | 必須包含 request 中每位使用者的 entry |
users.id | String | 必須與 request 的 id 相符 |
users.typeId | String | 必須與 request 的 typeId 相符 |
users.screenName | String | 使用者在平台上的 screen name 或 username |
users.email | Array | 已知電子郵件地址,type 可為 Business、Home 或 Work |
users.ipCaptureEvent | Array | 與使用者相關的 IP events,例如登入與註冊。eventName 可為 Login、Registration、Purchase、Upload、Other 或 Unknown。Coop 也會在有 schema 對應時,將 User Item 的 ipAddress Field role 以 Report incident time 的 Unknown event 加入 |
users.data | Object | 使用者的原始 Item data |
media | Array | 若 request 包含媒體,必須為每項媒體提供 entry |
media.id | String | 必須與 request 的 id 相符 |
media.typeId | String | 必須與 request 的 typeId 相符 |
media.missing | Boolean | 媒體已無法取得時設為 true。若所有媒體都是 missing: true,不會提交 CyberTip |
media.publiclyAvailable | Boolean | 通報當時,媒體是否可在平台公開存取 |
media.fileName | String | 媒體原始檔名 |
media.additionalInfo | Array<String> | 納入 NCMEC file details 的媒體補充脈絡 |
media.ipCaptureEvent | Array | 與媒體 Item 相關的 IP events。若有 schema 對應,Coop 也會將 Media Item 的 ipAddress Field role 以媒體 createdAt 的 Upload event 加入 |
media.fileDetails | Object | 檔案雜湊資訊,格式為 { hash, hashType } |
additionalFiles | Array | 上傳至 NCMEC 的補充佐證檔案,例如螢幕截圖 |
messages | Array | Conversation thread 脈絡中的 message-level IP address data |
additionalInfo | String | 納入 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. § 2258A 與 REPORT 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 通報的使用者 |
reportedMedia | CyberTip 包含的所有媒體 Items |
reportId | NCMEC 指派的 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 天內作成
上線前最低檢查
- 使用安全測試資料,在 NCMEC test endpoint 完整驗證路由、Additional Info、媒體缺漏、signature、Report 儲存、權限與重試
- 確認 test 與 production credentials 分離,且
NCMEC_ENV變更有人工核准、紀錄與回復程序 - 驗證 webhook 只接受 Coop 正確簽署的 request,並設定 timeout、重送冪等性、告警與失敗處理
- 確認 Report XML、IP、電子郵件、媒體、EXIF、CSV 與補充檔案的存取權、加密、備份、保存與刪除
- 確認至少有兩位適當角色人員可處理事件,但不因此擴張不必要的兒少安全資料存取
- 由法律與兒少安全專業人員書面確認正式通報與 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 定義採用下列結構時,表現最佳。
- Overview:Policy 主題簡介
- Definition of Terms:精確定義關鍵詞語與片語
- Interpretation of Language:說明如何處理模糊語言
- 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
| 欄位 | 英文來源所列資訊 |
|---|---|
| Model | CoPE-A-9B |
| Version | 1.x |
| Release date | 2025 年 7 月 20 日 |
| Training data | 約 60,000 個獨特 Policy/內容組合標籤,混合自動與人工標註,涵蓋仇恨言論、性內容、自傷、騷擾與毒性 |
| Annotation methodology | 針對 Policy 解釋而非記憶的新式訓練方法,使用互相衝突的 Policy 表述訓練 |
| Performance | Hate 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。
給採用者
-
在 server app 安裝整合套件,例如
npm install @roostorg/coop-integration-example -
從 integrations 設定檔啟用。Server 預設讀取
server/integrations.config.json,也可讀取INTEGRATIONS_CONFIG_PATH指定的路徑。結構範例如下{ "integrations": [ { "package": "@roostorg/coop-integration-example", "enabled": true } ] }本機開發若使用本機套件,可設定為
"package": "../coop-integration-example",路徑相對於設定檔所在目錄。不要把 secrets 放入設定檔。API keys 等資料應使用應用程式內的整合設定流程。 -
從使用者介面使用。整合會顯示在 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 的英文文件為準。






