<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Gdpr on Tarragon</title><link>https://tarrragon.github.io/blog/tags/gdpr/</link><description>Recent content in Gdpr on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/gdpr/index.xml" rel="self" type="application/rss+xml"/><item><title>去識別化策略</title><link>https://tarrragon.github.io/blog/monitoring/07-security-privacy/anonymization-strategy/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/07-security-privacy/anonymization-strategy/</guid><description>&lt;p>去識別化是把監控資料中可以關聯到特定個人的欄位，轉換成無法回溯到個人但仍保留分析價值的形式。去識別化和 &lt;a href="https://tarrragon.github.io/blog/monitoring/knowledge-cards/redaction/" data-link-title="Redaction" data-link-desc="說明在事件資料離開 client 之前把敏感欄位的值替換成遮罩或移除的機制">redaction&lt;/a> 的差別在於：redaction 完全移除資訊（&lt;code>[REDACTED]&lt;/code>），去識別化保留結構化的資訊但移除可識別性。&lt;/p>
&lt;h2 id="ip-截斷">IP 截斷&lt;/h2>
&lt;p>IP 位址是最常見的個人識別欄位。完整的 IPv4 位址（&lt;code>192.168.1.50&lt;/code>）可以定位到特定的網路和裝置；截斷後的 IP（&lt;code>192.168.1.0&lt;/code>）保留網段資訊但無法定位到特定裝置。&lt;/p>
&lt;h3 id="截斷策略">截斷策略&lt;/h3>
&lt;p>&lt;strong>IPv4 末八位清零&lt;/strong>：&lt;code>192.168.1.50&lt;/code> → &lt;code>192.168.1.0&lt;/code>。保留 /24 網段資訊，足以判斷「使用者在哪個網段」但無法定位到特定裝置。Google Analytics 採用這個策略。&lt;/p>
&lt;p>&lt;strong>IPv4 末十六位清零&lt;/strong>：&lt;code>192.168.1.50&lt;/code> → &lt;code>192.168.0.0&lt;/code>。更強的去識別化，但地理定位精度降低到城市級。&lt;/p>
&lt;p>&lt;strong>IPv6&lt;/strong>：截斷更多位元。IPv6 的後 80 位通常包含 MAC 位址衍生的 interface ID — 截斷到 /48 前綴保留 ISP 資訊，移除裝置識別。&lt;/p>
&lt;h3 id="實作位置">實作位置&lt;/h3>
&lt;p>IP 截斷應在 collector 收到事件後、寫入儲存前執行。SDK 端不做 IP 截斷 — SDK 通常不知道自己的外部 IP（知道的是 NAT 後的內部 IP），外部 IP 是 collector 從 HTTP request 的 source IP 取得的。&lt;/p>
&lt;h2 id="user-agent-簡化">User Agent 簡化&lt;/h2>
&lt;p>User agent 字串包含瀏覽器版本、OS 版本、裝置型號 — 組合起來可能形成唯一的 fingerprint。簡化 user agent 保留有用的分類資訊（「iOS 17 上的 Safari」），移除可用於 fingerprinting 的細節（「iPhone 15 Pro Max, Build/22A3354」）。&lt;/p>
&lt;h3 id="簡化規則">簡化規則&lt;/h3>
&lt;p>保留：平台（iOS / Android / Windows / macOS）、主要版本號（iOS 17、Android 14）、瀏覽器類型（Safari / Chrome / Firefox）。&lt;/p>
&lt;p>移除：minor version、build number、裝置型號、CPU 架構、語言設定。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">原始：Mozilla/5.0 (iPhone; CPU iPhone OS 17_4_1 like Mac OS X)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">簡化：iOS/17 Safari&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="stack-trace-路徑清理">Stack Trace 路徑清理&lt;/h2>
&lt;p>Error 事件的 stack trace 包含檔案路徑。檔案路徑可能洩漏部署結構（&lt;code>/home/deploy_user/app/v2.3.1/src/...&lt;/code>）或開發者的個人資訊（&lt;code>/Users/alice/projects/...&lt;/code>）。&lt;/p>
&lt;h3 id="清理規則">清理規則&lt;/h3>
&lt;p>&lt;strong>移除使用者目錄前綴&lt;/strong>：&lt;code>/Users/alice/projects/app/src/main.dart:42&lt;/code> → &lt;code>src/main.dart:42&lt;/code>。保留 source file 相對路徑和行號，移除使用者名稱。&lt;/p>
&lt;p>&lt;strong>移除部署路徑前綴&lt;/strong>：&lt;code>/opt/deploy/releases/20260619/app/lib/...&lt;/code> → &lt;code>lib/...&lt;/code>。保留程式碼結構，移除部署細節。&lt;/p>
&lt;p>&lt;strong>統一 path separator&lt;/strong>：Windows 路徑（&lt;code>C:\Users\...&lt;/code>）和 Unix 路徑（&lt;code>/home/...&lt;/code>）統一處理。&lt;/p>
&lt;p>清理規則用正則表達式匹配常見的路徑前綴模式，替換為空字串。自訂的部署路徑格式需要在 collector 設定中額外註冊。&lt;/p>
&lt;h2 id="session-uuid">Session UUID&lt;/h2>
&lt;p>Session ID 用於關聯同一次使用中的多個事件。UUID v4（隨機產生）作為 session ID，沒有可預測性、沒有順序性、無法回推使用者身份。&lt;/p>
&lt;h3 id="session-id-的生命週期">Session ID 的生命週期&lt;/h3>
&lt;p>SDK 在初始化時產生一個 UUID v4 作為 session ID，所有事件附帶這個 ID。App 重新啟動時產生新的 session ID — 前後兩次使用的事件無法關聯。&lt;/p>
&lt;p>這個設計讓分析粒度限制在「一次使用」而非「一個使用者」。如果需要跨 session 關聯（例如計算 DAU），需要另一個 persistent ID — 但 persistent ID 本身就是可識別資訊，需要使用者同意。&lt;/p>
&lt;h3 id="避免使用可識別的-id">避免使用可識別的 ID&lt;/h3>
&lt;p>裝置 ID（IDFA / GAID）、安裝 ID、使用者帳號 — 這些可以關聯到特定個人，不適合作為監控系統的 session ID。使用 UUID v4 確保 session ID 的唯一性來自隨機性而非身份。&lt;/p>
&lt;p>去識別化是資料保護的一環，另一環是在資料離開 client 之前就處理 — &lt;a href="https://tarrragon.github.io/blog/monitoring/07-security-privacy/sdk-redaction-api/" data-link-title="SDK Redaction API 設計" data-link-desc="預設 redaction rule 過濾已知敏感欄位、自訂 pattern 擴展應用特有的 secret 格式 — redaction 在 SDK 端執行，敏感資料不離開 client">SDK Redaction API 設計&lt;/a>從 SDK 端攔截敏感欄位。法規層面的具體要求見 &lt;a href="https://tarrragon.github.io/blog/monitoring/07-security-privacy/gdpr-minimization/" data-link-title="GDPR 最小化原則的工程落地" data-link-desc="資料最小化、目的限制、儲存限制 — GDPR 三個核心原則在監控系統的工程實作方式">GDPR 最小化原則的工程落地&lt;/a>。去識別化完成後的資料才能用於&lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">行為分析&lt;/a> — 這是商業利用的入場條件。&lt;/p></description><content:encoded><![CDATA[<p>去識別化是把監控資料中可以關聯到特定個人的欄位，轉換成無法回溯到個人但仍保留分析價值的形式。去識別化和 <a href="/blog/monitoring/knowledge-cards/redaction/" data-link-title="Redaction" data-link-desc="說明在事件資料離開 client 之前把敏感欄位的值替換成遮罩或移除的機制">redaction</a> 的差別在於：redaction 完全移除資訊（<code>[REDACTED]</code>），去識別化保留結構化的資訊但移除可識別性。</p>
<h2 id="ip-截斷">IP 截斷</h2>
<p>IP 位址是最常見的個人識別欄位。完整的 IPv4 位址（<code>192.168.1.50</code>）可以定位到特定的網路和裝置；截斷後的 IP（<code>192.168.1.0</code>）保留網段資訊但無法定位到特定裝置。</p>
<h3 id="截斷策略">截斷策略</h3>
<p><strong>IPv4 末八位清零</strong>：<code>192.168.1.50</code> → <code>192.168.1.0</code>。保留 /24 網段資訊，足以判斷「使用者在哪個網段」但無法定位到特定裝置。Google Analytics 採用這個策略。</p>
<p><strong>IPv4 末十六位清零</strong>：<code>192.168.1.50</code> → <code>192.168.0.0</code>。更強的去識別化，但地理定位精度降低到城市級。</p>
<p><strong>IPv6</strong>：截斷更多位元。IPv6 的後 80 位通常包含 MAC 位址衍生的 interface ID — 截斷到 /48 前綴保留 ISP 資訊，移除裝置識別。</p>
<h3 id="實作位置">實作位置</h3>
<p>IP 截斷應在 collector 收到事件後、寫入儲存前執行。SDK 端不做 IP 截斷 — SDK 通常不知道自己的外部 IP（知道的是 NAT 後的內部 IP），外部 IP 是 collector 從 HTTP request 的 source IP 取得的。</p>
<h2 id="user-agent-簡化">User Agent 簡化</h2>
<p>User agent 字串包含瀏覽器版本、OS 版本、裝置型號 — 組合起來可能形成唯一的 fingerprint。簡化 user agent 保留有用的分類資訊（「iOS 17 上的 Safari」），移除可用於 fingerprinting 的細節（「iPhone 15 Pro Max, Build/22A3354」）。</p>
<h3 id="簡化規則">簡化規則</h3>
<p>保留：平台（iOS / Android / Windows / macOS）、主要版本號（iOS 17、Android 14）、瀏覽器類型（Safari / Chrome / Firefox）。</p>
<p>移除：minor version、build number、裝置型號、CPU 架構、語言設定。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">原始：Mozilla/5.0 (iPhone; CPU iPhone OS 17_4_1 like Mac OS X)
</span></span><span class="line"><span class="ln">2</span><span class="cl">簡化：iOS/17 Safari</span></span></code></pre></div><h2 id="stack-trace-路徑清理">Stack Trace 路徑清理</h2>
<p>Error 事件的 stack trace 包含檔案路徑。檔案路徑可能洩漏部署結構（<code>/home/deploy_user/app/v2.3.1/src/...</code>）或開發者的個人資訊（<code>/Users/alice/projects/...</code>）。</p>
<h3 id="清理規則">清理規則</h3>
<p><strong>移除使用者目錄前綴</strong>：<code>/Users/alice/projects/app/src/main.dart:42</code> → <code>src/main.dart:42</code>。保留 source file 相對路徑和行號，移除使用者名稱。</p>
<p><strong>移除部署路徑前綴</strong>：<code>/opt/deploy/releases/20260619/app/lib/...</code> → <code>lib/...</code>。保留程式碼結構，移除部署細節。</p>
<p><strong>統一 path separator</strong>：Windows 路徑（<code>C:\Users\...</code>）和 Unix 路徑（<code>/home/...</code>）統一處理。</p>
<p>清理規則用正則表達式匹配常見的路徑前綴模式，替換為空字串。自訂的部署路徑格式需要在 collector 設定中額外註冊。</p>
<h2 id="session-uuid">Session UUID</h2>
<p>Session ID 用於關聯同一次使用中的多個事件。UUID v4（隨機產生）作為 session ID，沒有可預測性、沒有順序性、無法回推使用者身份。</p>
<h3 id="session-id-的生命週期">Session ID 的生命週期</h3>
<p>SDK 在初始化時產生一個 UUID v4 作為 session ID，所有事件附帶這個 ID。App 重新啟動時產生新的 session ID — 前後兩次使用的事件無法關聯。</p>
<p>這個設計讓分析粒度限制在「一次使用」而非「一個使用者」。如果需要跨 session 關聯（例如計算 DAU），需要另一個 persistent ID — 但 persistent ID 本身就是可識別資訊，需要使用者同意。</p>
<h3 id="避免使用可識別的-id">避免使用可識別的 ID</h3>
<p>裝置 ID（IDFA / GAID）、安裝 ID、使用者帳號 — 這些可以關聯到特定個人，不適合作為監控系統的 session ID。使用 UUID v4 確保 session ID 的唯一性來自隨機性而非身份。</p>
<p>去識別化是資料保護的一環，另一環是在資料離開 client 之前就處理 — <a href="/blog/monitoring/07-security-privacy/sdk-redaction-api/" data-link-title="SDK Redaction API 設計" data-link-desc="預設 redaction rule 過濾已知敏感欄位、自訂 pattern 擴展應用特有的 secret 格式 — redaction 在 SDK 端執行，敏感資料不離開 client">SDK Redaction API 設計</a>從 SDK 端攔截敏感欄位。法規層面的具體要求見 <a href="/blog/monitoring/07-security-privacy/gdpr-minimization/" data-link-title="GDPR 最小化原則的工程落地" data-link-desc="資料最小化、目的限制、儲存限制 — GDPR 三個核心原則在監控系統的工程實作方式">GDPR 最小化原則的工程落地</a>。去識別化完成後的資料才能用於<a href="/blog/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">行為分析</a> — 這是商業利用的入場條件。</p>
]]></content:encoded></item><item><title>GDPR 最小化原則的工程落地</title><link>https://tarrragon.github.io/blog/monitoring/07-security-privacy/gdpr-minimization/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/07-security-privacy/gdpr-minimization/</guid><description>&lt;p>GDPR 的資料最小化原則要求「只收集達成特定目的所需的最少資料」。這個法律原則轉譯到監控系統的工程實作，影響三個設計決策：收集什麼欄位、保留多久、誰可以存取。&lt;/p>
&lt;h2 id="資料最小化只收集需要的欄位">資料最小化：只收集需要的欄位&lt;/h2>
&lt;p>資料最小化的工程落地是「每個收集的欄位都要能回答：這個欄位用來做什麼決策？」。如果一個欄位只是「可能有用」但沒有明確的消費場景，就不應該收集。&lt;/p>
&lt;h3 id="正面表列-vs-負面排除">正面表列 vs 負面排除&lt;/h3>
&lt;p>正面表列（allowlist）是列出「收集哪些欄位」— 只收集清單上的欄位，其他全部不收。&lt;/p>
&lt;p>負面排除（denylist）是列出「不收集哪些欄位」— 預設收集所有欄位，排除清單上的。&lt;/p>
&lt;p>GDPR 的精神更接近正面表列 — 每個收集行為需要有正當理由（lawful basis）。工程上的實作方式是：事件 schema 定義哪些欄位是允許的，不在 schema 中的欄位在 collector 端丟棄。&lt;/p>
&lt;h3 id="sdk-端的最小化">SDK 端的最小化&lt;/h3>
&lt;p>SDK 端的最小化更主動 — 在事件產生時就只包含必要的欄位，而非送到 collector 再過濾。&lt;/p>
&lt;p>設計 SDK 的 event API 時，不提供「送任意 key-value」的 free-form API，而是提供結構化的 API：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">// free-form（難以控制收集了什麼）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">monitor.event(&amp;#39;login&amp;#39;, data: {&amp;#39;email&amp;#39;: email, &amp;#39;ip&amp;#39;: ip, &amp;#39;device&amp;#39;: device, ...})
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">// 結構化（schema 控制收集範圍）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">monitor.event(&amp;#39;login&amp;#39;, loginMethod: &amp;#39;biometric&amp;#39;, success: true)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>結構化 API 的參數在 SDK 設計時就決定了收集範圍，code review 時可以檢查「為什麼這個 event 需要這個參數」。&lt;/p>
&lt;h2 id="目的限制收集的資料只用於聲明的目的">目的限制：收集的資料只用於聲明的目的&lt;/h2>
&lt;p>目的限制要求資料只用於收集時聲明的目的。監控系統收集事件的目的通常是 debug 和效能監控 — 如果之後要用同一份資料做行為分析或廣告投放，需要額外的法律基礎（通常是使用者同意）。&lt;/p>
&lt;h3 id="工程落地">工程落地&lt;/h3>
&lt;p>目的限制在工程上的實作是「不同目的的資料分開儲存、分開授權」。&lt;/p>
&lt;p>Debug 用的 error 事件和行為分析用的 event 事件存在不同的儲存位置（不同的 JSONL 檔案或不同的資料庫 table）。Debug 用途的 access 不需要使用者同意（legitimate interest）；行為分析用途的 access 需要使用者同意。&lt;/p>
&lt;p>分開儲存讓「使用者撤回行為分析同意」的工程操作變簡單 — 刪除行為分析的儲存，不影響 debug 儲存。&lt;/p>
&lt;h2 id="儲存限制不保留超過必要期間的資料">儲存限制：不保留超過必要期間的資料&lt;/h2>
&lt;p>儲存限制要求資料只保留達成目的所需的最短期間。監控資料的合理保留期間依用途不同：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>用途&lt;/th>
 &lt;th>合理保留期間&lt;/th>
 &lt;th>理由&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Debug&lt;/td>
 &lt;td>30-90 天&lt;/td>
 &lt;td>大部分 bug 在 30 天內被發現和修復&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>效能趨勢&lt;/td>
 &lt;td>6-12 個月&lt;/td>
 &lt;td>季節性趨勢需要至少一年的資料&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>行為分析&lt;/td>
 &lt;td>依同意期間&lt;/td>
 &lt;td>使用者同意到期就刪除&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>合規審計&lt;/td>
 &lt;td>依法規要求（通常 1-7 年）&lt;/td>
 &lt;td>法規指定的最短保留期間&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="自動清理">自動清理&lt;/h3>
&lt;p>Collector 的儲存清理應該自動化 — 手動清理依賴人記得執行，最終會被遺忘。&lt;/p>
&lt;p>JSONL 儲存用「一天一檔」的命名（&lt;code>events-2026-06-19.jsonl&lt;/code>），清理腳本每天刪除超過保留期限的檔案。Cron job 或 systemd timer 定期執行。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>去識別化技術 → &lt;a href="https://tarrragon.github.io/blog/monitoring/07-security-privacy/anonymization-strategy/" data-link-title="去識別化策略" data-link-desc="IP 截斷 / user agent 簡化 / stack trace 路徑清理 / session UUID — 四種去識別化技術的適用場景和實作方式">去識別化策略&lt;/a>&lt;/li>
&lt;li>監控資料洩漏的威脅分析 → &lt;a href="https://tarrragon.github.io/blog/monitoring/07-security-privacy/monitoring-data-threat-model/" data-link-title="監控資料洩漏的 Threat Model" data-link-desc="監控系統本身是攻擊面 — 四個威脅場景（傳輸竊聽 / 儲存入侵 / endpoint 濫用 / 內部越權存取）的風險評估和防護措施">監控資料洩漏的 threat model&lt;/a>&lt;/li>
&lt;li>Collector 的儲存設計 → &lt;a href="https://tarrragon.github.io/blog/monitoring/04-collector/" data-link-title="模組四：Collector 設計" data-link-desc="收 → 驗 → 存 → 查 → 觸發的完整鏈路 — Go 單一 binary、可插拔 Storage Backend、rule engine">模組四 Collector 設計&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>GDPR 的資料最小化原則要求「只收集達成特定目的所需的最少資料」。這個法律原則轉譯到監控系統的工程實作，影響三個設計決策：收集什麼欄位、保留多久、誰可以存取。</p>
<h2 id="資料最小化只收集需要的欄位">資料最小化：只收集需要的欄位</h2>
<p>資料最小化的工程落地是「每個收集的欄位都要能回答：這個欄位用來做什麼決策？」。如果一個欄位只是「可能有用」但沒有明確的消費場景，就不應該收集。</p>
<h3 id="正面表列-vs-負面排除">正面表列 vs 負面排除</h3>
<p>正面表列（allowlist）是列出「收集哪些欄位」— 只收集清單上的欄位，其他全部不收。</p>
<p>負面排除（denylist）是列出「不收集哪些欄位」— 預設收集所有欄位，排除清單上的。</p>
<p>GDPR 的精神更接近正面表列 — 每個收集行為需要有正當理由（lawful basis）。工程上的實作方式是：事件 schema 定義哪些欄位是允許的，不在 schema 中的欄位在 collector 端丟棄。</p>
<h3 id="sdk-端的最小化">SDK 端的最小化</h3>
<p>SDK 端的最小化更主動 — 在事件產生時就只包含必要的欄位，而非送到 collector 再過濾。</p>
<p>設計 SDK 的 event API 時，不提供「送任意 key-value」的 free-form API，而是提供結構化的 API：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">// free-form（難以控制收集了什麼）
</span></span><span class="line"><span class="ln">2</span><span class="cl">monitor.event(&#39;login&#39;, data: {&#39;email&#39;: email, &#39;ip&#39;: ip, &#39;device&#39;: device, ...})
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">// 結構化（schema 控制收集範圍）
</span></span><span class="line"><span class="ln">5</span><span class="cl">monitor.event(&#39;login&#39;, loginMethod: &#39;biometric&#39;, success: true)</span></span></code></pre></div><p>結構化 API 的參數在 SDK 設計時就決定了收集範圍，code review 時可以檢查「為什麼這個 event 需要這個參數」。</p>
<h2 id="目的限制收集的資料只用於聲明的目的">目的限制：收集的資料只用於聲明的目的</h2>
<p>目的限制要求資料只用於收集時聲明的目的。監控系統收集事件的目的通常是 debug 和效能監控 — 如果之後要用同一份資料做行為分析或廣告投放，需要額外的法律基礎（通常是使用者同意）。</p>
<h3 id="工程落地">工程落地</h3>
<p>目的限制在工程上的實作是「不同目的的資料分開儲存、分開授權」。</p>
<p>Debug 用的 error 事件和行為分析用的 event 事件存在不同的儲存位置（不同的 JSONL 檔案或不同的資料庫 table）。Debug 用途的 access 不需要使用者同意（legitimate interest）；行為分析用途的 access 需要使用者同意。</p>
<p>分開儲存讓「使用者撤回行為分析同意」的工程操作變簡單 — 刪除行為分析的儲存，不影響 debug 儲存。</p>
<h2 id="儲存限制不保留超過必要期間的資料">儲存限制：不保留超過必要期間的資料</h2>
<p>儲存限制要求資料只保留達成目的所需的最短期間。監控資料的合理保留期間依用途不同：</p>
<table>
  <thead>
      <tr>
          <th>用途</th>
          <th>合理保留期間</th>
          <th>理由</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Debug</td>
          <td>30-90 天</td>
          <td>大部分 bug 在 30 天內被發現和修復</td>
      </tr>
      <tr>
          <td>效能趨勢</td>
          <td>6-12 個月</td>
          <td>季節性趨勢需要至少一年的資料</td>
      </tr>
      <tr>
          <td>行為分析</td>
          <td>依同意期間</td>
          <td>使用者同意到期就刪除</td>
      </tr>
      <tr>
          <td>合規審計</td>
          <td>依法規要求（通常 1-7 年）</td>
          <td>法規指定的最短保留期間</td>
      </tr>
  </tbody>
</table>
<h3 id="自動清理">自動清理</h3>
<p>Collector 的儲存清理應該自動化 — 手動清理依賴人記得執行，最終會被遺忘。</p>
<p>JSONL 儲存用「一天一檔」的命名（<code>events-2026-06-19.jsonl</code>），清理腳本每天刪除超過保留期限的檔案。Cron job 或 systemd timer 定期執行。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>去識別化技術 → <a href="/blog/monitoring/07-security-privacy/anonymization-strategy/" data-link-title="去識別化策略" data-link-desc="IP 截斷 / user agent 簡化 / stack trace 路徑清理 / session UUID — 四種去識別化技術的適用場景和實作方式">去識別化策略</a></li>
<li>監控資料洩漏的威脅分析 → <a href="/blog/monitoring/07-security-privacy/monitoring-data-threat-model/" data-link-title="監控資料洩漏的 Threat Model" data-link-desc="監控系統本身是攻擊面 — 四個威脅場景（傳輸竊聽 / 儲存入侵 / endpoint 濫用 / 內部越權存取）的風險評估和防護措施">監控資料洩漏的 threat model</a></li>
<li>Collector 的儲存設計 → <a href="/blog/monitoring/04-collector/" data-link-title="模組四：Collector 設計" data-link-desc="收 → 驗 → 存 → 查 → 觸發的完整鏈路 — Go 單一 binary、可插拔 Storage Backend、rule engine">模組四 Collector 設計</a></li>
</ul>
]]></content:encoded></item><item><title>模組七：資安與隱私</title><link>https://tarrragon.github.io/blog/monitoring/07-security-privacy/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/07-security-privacy/</guid><description>&lt;p>回答「蒐集的資料本身就是風險資產，怎麼保護」。三層防護：SDK 端 redaction → transport 加密 → collector access control。&lt;/p>
&lt;h2 id="待寫章節">待寫章節&lt;/h2>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> SDK redaction API 設計（預設 redaction rule + 自訂 pattern）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Transport 安全（HTTPS / basic auth / 同區網也要加密的理由）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Collector access control 實作（認證 / 授權 / access log）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> 去識別化策略（IP 截斷 / user agent 簡化 / stack trace 路徑清理 / session UUID）&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> GDPR 最小化原則的工程落地&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> 「監控資料洩漏」的 threat model&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Client-side SDK 認證的根本限制（credential 必然暴露、多層緩解策略）&lt;/li>
&lt;/ul>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">backend 07 資安&lt;/a>：server-side 的 secret management 跟本模組的 redaction 互補&lt;/li>
&lt;li>← &lt;a href="https://tarrragon.github.io/blog/ux-design/03-input-mechanism/" data-link-title="模組三：輸入機制設計" data-link-desc="Keyboard type / submit model / IME policy / special keys — 輸入機制是設計產物，影響 UI layout 和 protocol">ux-design 模組三 輸入機制&lt;/a>：IME 個人化學習 = secret 洩漏&lt;/li>
&lt;li>← &lt;a href="https://tarrragon.github.io/blog/testing/02-client-observability/" data-link-title="模組二：客戶端可觀測性" data-link-desc="連線生命週期 log、protocol 訊息 log、使用者行為 log — log 設計是功能規格的一部分">testing 模組二 客戶端可觀測性&lt;/a>：log 內容可能含 secret，需要 redaction&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">monitoring 模組八&lt;/a>：去識別化是商業利用的入場條件&lt;/li>
&lt;li>待建連結 → &lt;code>compliance/&lt;/code>（隱私法規教學分類）&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>回答「蒐集的資料本身就是風險資產，怎麼保護」。三層防護：SDK 端 redaction → transport 加密 → collector access control。</p>
<h2 id="待寫章節">待寫章節</h2>
<ul>
<li><input checked="" disabled="" type="checkbox"> SDK redaction API 設計（預設 redaction rule + 自訂 pattern）</li>
<li><input checked="" disabled="" type="checkbox"> Transport 安全（HTTPS / basic auth / 同區網也要加密的理由）</li>
<li><input checked="" disabled="" type="checkbox"> Collector access control 實作（認證 / 授權 / access log）</li>
<li><input checked="" disabled="" type="checkbox"> 去識別化策略（IP 截斷 / user agent 簡化 / stack trace 路徑清理 / session UUID）</li>
<li><input checked="" disabled="" type="checkbox"> GDPR 最小化原則的工程落地</li>
<li><input checked="" disabled="" type="checkbox"> 「監控資料洩漏」的 threat model</li>
<li><input checked="" disabled="" type="checkbox"> Client-side SDK 認證的根本限制（credential 必然暴露、多層緩解策略）</li>
</ul>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">backend 07 資安</a>：server-side 的 secret management 跟本模組的 redaction 互補</li>
<li>← <a href="/blog/ux-design/03-input-mechanism/" data-link-title="模組三：輸入機制設計" data-link-desc="Keyboard type / submit model / IME policy / special keys — 輸入機制是設計產物，影響 UI layout 和 protocol">ux-design 模組三 輸入機制</a>：IME 個人化學習 = secret 洩漏</li>
<li>← <a href="/blog/testing/02-client-observability/" data-link-title="模組二：客戶端可觀測性" data-link-desc="連線生命週期 log、protocol 訊息 log、使用者行為 log — log 設計是功能規格的一部分">testing 模組二 客戶端可觀測性</a>：log 內容可能含 secret，需要 redaction</li>
<li>→ <a href="/blog/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">monitoring 模組八</a>：去識別化是商業利用的入場條件</li>
<li>待建連結 → <code>compliance/</code>（隱私法規教學分類）</li>
</ul>
]]></content:encoded></item><item><title>PostgreSQL Multi-Region GDPR Rollout：政策驅動的 migration 屬本 methodology 嗎</title><link>https://tarrragon.github.io/blog/backend/01-database/vendors/postgresql/multi-region-gdpr-rollout/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/vendors/postgresql/multi-region-gdpr-rollout/</guid><description>&lt;blockquote>
&lt;p>本文是 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/vendors/postgresql/" data-link-title="PostgreSQL" data-link-desc="多用途 OLTP 主流關聯式資料庫、MVCC、豐富 SQL 特性、是 Aurora / Cosmos DB / Spanner / CockroachDB / Aurora DSQL 的相容目標">PostgreSQL&lt;/a> overview 的 implementation-layer deep article。同時是 &lt;a href="https://tarrragon.github.io/blog/report/data-topology-as-audit-dimension/" data-link-title="Data topology 是 process content 的第 6 audit 維度" data-link-desc="Process content 的 diff dimension audit 原本 5 維（schema / operational / paradigm / components / application change）漏了 *data topology* — 資料在 cluster / partition / region 之間的分佈拓樸；topology 不在既有 5 維任一個、但決定 re-sharding / partition redesign / multi-region rollout 的結構；本卡擴 audit 到 6 維、新增 Type F「Topology re-layout」結構">#128 self-aware limitation&lt;/a> 第 1 點「6 維仍可能漏類（identity / consistency / residency 三軸候選）」的 &lt;em>residency 軸驗證&lt;/em>、跟 &lt;a href="https://tarrragon.github.io/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration playbook methodology「何時不該套」段&lt;/a> 對「政策合規驅動」是否在 methodology scope 的反思。&lt;/p>&lt;/blockquote>
&lt;h2 id="政策驅動的-migration-屬本-methodology-嗎">政策驅動的 migration 屬本 methodology 嗎&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">Migration playbook methodology&lt;/a> 「何時不該套」段曾把「compliance-driven migration」歸為排除情境、後來改寫為「不在排除範圍 — 法規驅動只是 driver、資料層仍走 type A-E 之一」。本文是該改寫的 &lt;em>正面實證&lt;/em> — GDPR EU residency 強制需求驅動 single-region → multi-region rollout、本文是 &lt;em>政策驅動但仍走 audit + type 對映流程&lt;/em> 的 case study。&lt;/p>
&lt;p>但 reviewer D 在第三輪 audit 提出：residency 不只是 &lt;em>driver&lt;/em>、本身是 &lt;em>cross-cutting constraint&lt;/em>、反向約束 topology + operational + schema；該不該升 &lt;em>獨立 audit 軸&lt;/em>？本文是該議題的 dogfood。&lt;/p>
&lt;h2 id="三層約束driver--topology--contract">三層約束：driver / topology / contract&lt;/h2>
&lt;p>GDPR 對 PostgreSQL multi-region rollout 的影響在三個層次：&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是 <a href="/blog/backend/01-database/vendors/postgresql/" data-link-title="PostgreSQL" data-link-desc="多用途 OLTP 主流關聯式資料庫、MVCC、豐富 SQL 特性、是 Aurora / Cosmos DB / Spanner / CockroachDB / Aurora DSQL 的相容目標">PostgreSQL</a> overview 的 implementation-layer deep article。同時是 <a href="/blog/report/data-topology-as-audit-dimension/" data-link-title="Data topology 是 process content 的第 6 audit 維度" data-link-desc="Process content 的 diff dimension audit 原本 5 維（schema / operational / paradigm / components / application change）漏了 *data topology* — 資料在 cluster / partition / region 之間的分佈拓樸；topology 不在既有 5 維任一個、但決定 re-sharding / partition redesign / multi-region rollout 的結構；本卡擴 audit 到 6 維、新增 Type F「Topology re-layout」結構">#128 self-aware limitation</a> 第 1 點「6 維仍可能漏類（identity / consistency / residency 三軸候選）」的 <em>residency 軸驗證</em>、跟 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration playbook methodology「何時不該套」段</a> 對「政策合規驅動」是否在 methodology scope 的反思。</p></blockquote>
<h2 id="政策驅動的-migration-屬本-methodology-嗎">政策驅動的 migration 屬本 methodology 嗎</h2>
<p><a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">Migration playbook methodology</a> 「何時不該套」段曾把「compliance-driven migration」歸為排除情境、後來改寫為「不在排除範圍 — 法規驅動只是 driver、資料層仍走 type A-E 之一」。本文是該改寫的 <em>正面實證</em> — GDPR EU residency 強制需求驅動 single-region → multi-region rollout、本文是 <em>政策驅動但仍走 audit + type 對映流程</em> 的 case study。</p>
<p>但 reviewer D 在第三輪 audit 提出：residency 不只是 <em>driver</em>、本身是 <em>cross-cutting constraint</em>、反向約束 topology + operational + schema；該不該升 <em>獨立 audit 軸</em>？本文是該議題的 dogfood。</p>
<h2 id="三層約束driver--topology--contract">三層約束：driver / topology / contract</h2>
<p>GDPR 對 PostgreSQL multi-region rollout 的影響在三個層次：</p>
<ol>
<li><strong>Driver layer</strong>：EU 客戶資料必須 <em>物理上儲存在 EU</em>（GDPR Article 44-49）— 觸發 multi-region migration 的根本理由</li>
<li><strong>Topology layer</strong>：跨 region replication 不能 <em>自由跨 region 複製</em> EU 客戶資料、必須按 GDPR scope 分區；topology 設計受合規約束</li>
<li><strong>Contract layer</strong>：審計能 <em>demonstrate</em> 「EU 資料在 EU」、操作日誌 + replication evidence 必須可追溯；application + ops contract 多出合規 obligation</li>
</ol>
<p>跑 <a href="/blog/report/content-structure-by-max-diff-dimension/" data-link-title="Process content 結構由最大差異維度決定、不是 universal phased" data-link-desc="跨 X process content（migration / upgrade / rollout / playbook）的結構由 source / target 之間 *差異維度組合* 決定、不存在 universal phased 模板；6 種 migration / process type 實證（schema 差 / drop-in / operational / multi-tool / paradigm / topology re-layout）跑出 6 種不同結構；寫作前必須做 *6 維 diff dimension audit* 才能決定結構、跳過會套錯模板">6 維 diff dimension audit</a> 對「single us-east → us-east + eu-west」：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評估</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>同 PostgreSQL、可能加 region column</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>HA / backup / monitoring 跨 region 重設計</td>
          <td><strong>High</strong></td>
      </tr>
      <tr>
          <td>Paradigm</td>
          <td>同 OLTP RDBMS</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Components</td>
          <td>同 PostgreSQL instance + Patroni</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>Routing logic by user region、必改</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>Single → multi-region replication</td>
          <td><strong>High</strong></td>
      </tr>
      <tr>
          <td><strong>Residency contract</strong></td>
          <td><strong>EU 資料禁止離開 EU、log + replication 範圍受約束</strong></td>
          <td><strong>High</strong></td>
      </tr>
  </tbody>
</table>
<p>6 維 audit 抓不到「Residency contract = High」這軸。用既有 6 維歸類、會走 Type F multi-axis（topology + operational + application change 多 High）+ 政策合規補強段；但這個歸類 <em>漏掉合規對 topology / operational / application 的反向約束</em>：</p>
<ul>
<li>Topology layer：6 維只 audit 「topology 是否變動」、漏 audit 「topology 範圍是否受合規約束」</li>
<li>Operational layer：6 維只 audit 「operational 是否重設計」、漏 audit 「audit log / encryption / access control 是否符合合規要求」</li>
<li>Application layer：6 維只 audit 「application code 是否改」、漏 audit 「資料 routing 是否符合 residency rule」</li>
</ul>
<p><strong>Residency 不只是 driver、是 cross-cutting constraint</strong>、會反向約束其他 3-4 維、且帶獨立工作量（合規 evidence collection / DPIA / audit prep）。</p>
<h2 id="residency-axis-是否獨立3-個論據">Residency axis 是否獨立：3 個論據</h2>
<p><strong>Yes、residency 是獨立軸</strong>：</p>
<ol>
<li><strong>可獨立發生</strong>：原本 multi-region setup、新增「PCI 強制信用卡資料只能 us-east」、是 <em>純 residency 變更</em>、其他 6 維皆 Low（topology 不重設計、operational 不重設計、application 加 routing rule 即可）；但 residency 約束 routing + log 範圍</li>
<li><strong>驅動工作量分佈</strong>：本文 multi-region GDPR rollout 工作量分佈：
<ul>
<li>Topology setup（logical replication / region setup）：~25%</li>
<li>Operational redesign（HA / backup / monitoring）：~20%</li>
<li>Application routing change（region detection / data filter）：~15%</li>
<li><strong>Residency compliance（DPIA / audit log / access control / encryption / evidence）：~40%</strong></li>
</ul>
</li>
<li><strong>Cross-cutting nature</strong>：residency 不只影響「資料放哪」、影響：
<ul>
<li>Backup 可不可以 cross-region store（多數 GDPR 不允許）</li>
<li>Audit log 是否包含 EU PII（需 EU 端 log + 跨 region log filter）</li>
<li>Encryption key 是否可 cross-region share（多數情境不允許）</li>
<li>Application access logs 是否含 EU IP / user ID</li>
</ul>
</li>
</ol>
<p><strong>No、residency 可塞 operational + driver</strong>：</p>
<ul>
<li>反論：residency 是 operational 子議題、加 audit + replication scope 規則就好</li>
<li>拒絕：residency 反向約束 topology / application / operational、且帶獨立合規工作量（DPIA / cross-border transfer agreement / data subject rights）；不是單純 operational 子議題</li>
</ul>
<p>實證：本文 migration 工作量 40% 在 compliance、確認 residency 是 <em>獨立工作量主軸</em>。</p>
<h2 id="結構type-f-multi-axis--residency-compliance-獨立段">結構：Type F multi-axis + residency compliance 獨立段</h2>
<p>本文結構是 <em>Type F 為主</em>（topology high + operational high）+ <em>residency compliance 獨立段</em>（不在 6 維任一個）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">1. 政策驅動的 migration 屬本 methodology 嗎（meta-reflection 開頭）
</span></span><span class="line"><span class="ln">2</span><span class="cl">2. 三層約束：driver / topology / contract
</span></span><span class="line"><span class="ln">3</span><span class="cl">3. Residency axis 是否獨立的論據
</span></span><span class="line"><span class="ln">4</span><span class="cl">4. 結構 differentiator（Type F multi-axis + residency compliance 段）
</span></span><span class="line"><span class="ln">5</span><span class="cl">5. EU residency 對 topology / operational / application 的反向約束
</span></span><span class="line"><span class="ln">6</span><span class="cl">6. Migration 流程（含 DPIA 跟 evidence collection 階段）
</span></span><span class="line"><span class="ln">7</span><span class="cl">7. Production 故障演練
</span></span><span class="line"><span class="ln">8</span><span class="cl">8. Capacity / cost（含合規 audit cost）
</span></span><span class="line"><span class="ln">9</span><span class="cl">9. 整合 / 下一步</span></span></code></pre></div><p>9 章節、240-270 行。比標準 Type F 多 1 段（residency compliance）+ 1 段（meta-reflection）。</p>
<h2 id="eu-residency-對其他維度的反向約束">EU residency 對其他維度的反向約束</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">Residency rule → Topology constraint:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- EU customer data 不能 replicate to us-east
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- Backup of EU table 不能 store in non-EU region
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- Logical replication subscriber 在 us-east 必須 filter out EU data
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Residency rule → Operational constraint:
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">- Cross-region monitoring 不能 export EU PII to global SaaS (Datadog)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- Audit log 含 EU user_id 必須 store 在 EU
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- Encryption key (KMS) 不能 share 跨 region（EU 端用 EU KMS）
</span></span><span class="line"><span class="ln">10</span><span class="cl">- DBA / SRE access EU data 必須 from EU jurisdiction + 記 audit trail
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">Residency rule → Application constraint:
</span></span><span class="line"><span class="ln">13</span><span class="cl">- Application 必須 detect user region + route 對應 DB endpoint
</span></span><span class="line"><span class="ln">14</span><span class="cl">- Cross-region join / aggregate 對 EU user 必須走 EU 端 query
</span></span><span class="line"><span class="ln">15</span><span class="cl">- Data export feature 必須 reject 跨 region export request</span></span></code></pre></div><p>每條反向約束都是 <em>新工作量</em>、不在 6 維 audit 內。</p>
<h2 id="migration-流程含-dpia--evidence-collection">Migration 流程（含 DPIA + evidence collection）</h2>
<p>10 step、跨 5 個月：</p>
<table>
  <thead>
      <tr>
          <th>Phase</th>
          <th>Step</th>
          <th>對應 6 維 / 合規</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>0 Pre-migration</td>
          <td>1. DPIA（Data Protection Impact Assessment）</td>
          <td>Compliance pre-requisite</td>
      </tr>
      <tr>
          <td>0</td>
          <td>2. 法務 review 跨境傳輸 agreement</td>
          <td>Compliance</td>
      </tr>
      <tr>
          <td>1 Setup</td>
          <td>3. EU PostgreSQL cluster build + Patroni</td>
          <td>Operational + Topology</td>
      </tr>
      <tr>
          <td>1</td>
          <td>4. EU KMS + audit log + monitoring stack</td>
          <td>Operational + Residency</td>
      </tr>
      <tr>
          <td>2 Data</td>
          <td>5. Logical replication 設 filter（exclude EU table from us-east）</td>
          <td>Topology + Residency</td>
      </tr>
      <tr>
          <td>2</td>
          <td>6. Initial sync EU table 到 EU cluster</td>
          <td>Topology</td>
      </tr>
      <tr>
          <td>3 App</td>
          <td>7. Application 端加 region detection + routing</td>
          <td>Application change</td>
      </tr>
      <tr>
          <td>3</td>
          <td>8. Cross-region query banning（cross-region join 拒絕 EU table）</td>
          <td>Application + Residency</td>
      </tr>
      <tr>
          <td>4 Verify</td>
          <td>9. Compliance audit + evidence package</td>
          <td>Residency</td>
      </tr>
      <tr>
          <td>4</td>
          <td>10. DPO sign-off + DR drill</td>
          <td>Residency + Operational</td>
      </tr>
  </tbody>
</table>
<p>Step 1 + 9 + 10 是 <em>residency-specific</em>、不在既有 6 維內。</p>
<h2 id="production-故障演練">Production 故障演練</h2>
<h3 id="case-1replication-filter-漏-tableeu-資料-leak-到-us-east">Case 1：Replication filter 漏 table、EU 資料 leak 到 us-east</h3>
<p><strong>徵兆</strong>：6 個月後 internal audit 發現 us-east 端 <code>customers</code> table 含 EU 客戶資料；replication filter 設定漏改、新加的 <code>eu_customer_extensions</code> table 被自動 replicate 到 us-east。</p>
<p><strong>根因</strong>：PostgreSQL logical replication publication 預設 <code>FOR ALL TABLES</code>、新加的 table 自動納入；應該明示 <code>FOR TABLE list...</code> 並 GDPR review。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Publication 改 explicit table list</strong>：<code>CREATE PUBLICATION xxx FOR TABLE users, orders, ...</code>、不用 <code>FOR ALL TABLES</code></li>
<li><strong>Schema change review 加 GDPR check</strong>：每個 DDL PR 必須答「新 table 是否含 EU PII、是否該 filter」</li>
<li><strong>Replication monitor</strong>：定期跑 <code>SELECT * FROM pg_publication_tables</code> 對照 expected list、漂移立刻 alert</li>
<li><strong>Evidence collection</strong>：filter 配置 + audit log 留檔、出事 DPO 知道何時 leak</li>
</ol>
<h3 id="case-2backup-跨-region-store合規違規">Case 2：Backup 跨 region store、合規違規</h3>
<p><strong>徵兆</strong>：跑 1 年後 GDPR audit 抓到 EU table 的 backup 存在 us-west S3 bucket；違反 Article 44-49 限制。</p>
<p><strong>根因</strong>：pgBackRest 預設用 <em>global S3 bucket</em>（在 us-east-1）；EU PostgreSQL cluster backup 跑去 us-east、跨境傳輸無 transfer mechanism。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Per-region backup config</strong>：EU cluster 用 EU S3 bucket（eu-west-1）、寫進 pgBackRest config</li>
<li><strong>Backup test</strong>：每月跑一次 backup restore drill、validate backup 是 from EU region</li>
<li><strong>Bucket policy 強 enforce</strong>：EU bucket 加 <code>aws:RequestedRegion=eu-west-1</code> 強制 region match</li>
<li><strong>Audit log archive 同理</strong>：log shipping 也必須 region-respect</li>
</ol>
<h3 id="case-3monitor-saas-收集-eu-pii合規-alert">Case 3：Monitor SaaS 收集 EU PII、合規 alert</h3>
<p><strong>徵兆</strong>：Datadog APM 收集了 EU customer 端 request 含 user_email 在 trace、被 DPO catch、required to delete 過去 90 天的 Datadog data。</p>
<p><strong>根因</strong>：APM trace 預設收集 application context、含 PII；Datadog 是 us-east SaaS、PII 跨境到 Datadog us-east、違規。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>APM scrub PII</strong>：application 端在 trace 前 scrub user_email / user_id 替換成 hash</li>
<li><strong>EU-specific monitor stack</strong>：EU PostgreSQL + APM 用 Grafana on EU EKS、不送 Datadog</li>
<li><strong>跨 region SaaS use 必須 audit</strong>：所有外部 SaaS（Datadog / Sentry / NewRelic）必須 GDPR-friendly 配置</li>
<li><strong>Privacy by design</strong>：log / trace 預設 scrub PII、不是 opt-in</li>
</ol>
<h3 id="case-4cross-region-query-跑-eu--us-資料residency-違規">Case 4：Cross-region query 跑 EU + US 資料、residency 違規</h3>
<p><strong>徵兆</strong>：BI dashboard 跑跨 region aggregation query（EU sales + US sales）、PostgreSQL FDW 從 us-east cluster query EU cluster、EU 端 server log 顯示「PII export to us-east」。</p>
<p><strong>根因</strong>：開發者用 PostgreSQL Foreign Data Wrapper（FDW）方便跑跨 region query、不知道這在 GDPR 視為跨境 PII export。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Architecture: aggregate at edge</strong>：BI 跑 <em>per-region aggregate</em>、再在 BI layer compose（無 PII）；不直接跨 region join</li>
<li><strong>FDW 限制</strong>：disable FDW from us-east → EU cluster、enforce one-way data flow</li>
<li><strong>DBA access policy</strong>：DBA 不能直接 query EU cluster 從 us-east jumpbox</li>
<li><strong>Query audit</strong>：production query log 跑 PII detection（regex / NER）、發現跨境 export 立即 alert</li>
</ol>
<h3 id="case-5dr-drill-跨-region-failover暴露-residency-assumption-失敗">Case 5：DR drill 跨 region failover、暴露 residency assumption 失敗</h3>
<p><strong>徵兆</strong>：DR drill「EU 完全不可用、切到 us-east」執行後、發現 us-east 端 <em>沒 EU 資料</em> — 因為一直 strict residency filter；business 端 EU 客戶 24 小時無法服務。</p>
<p><strong>根因</strong>：strict GDPR residency 跟 strict DR availability 衝突 — 要 <em>跨 region DR</em> 就要 <em>跨 region 持有資料</em>、要 <em>strict residency</em> 就 <em>DR 範圍受限</em>。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>DR strategy revision</strong>：EU 端 multi-AZ within EU、不靠跨 region；EU region 全不可用情境接受 longer RTO</li>
<li><strong>Compliance + DR negotiation</strong>：跟 DPO / 法務談 <em>DR 跨境 short-window 是否可接受</em>、簽 cross-border transfer agreement</li>
<li><strong>Backup recovery 在 EU 內</strong>：EU 端 backup 跨 AZ store、不跨 region；EU AZ 災難用 EU 另一個 AZ 重建</li>
<li><strong>明示 RTO trade-off</strong>：EU customer SLA 寫「regional DR 內 RTO 1 小時、global DR 24-48 小時」、residency 跟 DR 是 <em>互斥取捨</em></li>
</ol>
<h2 id="capacity--cost">Capacity / cost</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Single region</th>
          <th>Multi-region GDPR-compliant</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Infrastructure cost</td>
          <td>baseline</td>
          <td>+60-100%（雙 cluster + cross-region replication）</td>
      </tr>
      <tr>
          <td>Operational FTE</td>
          <td>0.5-1</td>
          <td>1-2 FTE（雙 region SRE + compliance）</td>
      </tr>
      <tr>
          <td>Compliance cost</td>
          <td>0</td>
          <td>$50-200K USD setup（DPIA / audit / DPO time）+ ongoing</td>
      </tr>
      <tr>
          <td>Egress cost</td>
          <td>Low</td>
          <td>High（cross-region replication 流量）</td>
      </tr>
      <tr>
          <td>Application latency</td>
          <td>Single AZ</td>
          <td>EU customer 連 EU、低；US customer 連 US、低</td>
      </tr>
      <tr>
          <td>DR RTO</td>
          <td>30 分鐘 (single region)</td>
          <td>EU regional 1 小時 / global 24-48 小時</td>
      </tr>
      <tr>
          <td>Audit cost</td>
          <td>Minimal</td>
          <td>季度 DPIA + 年度 compliance audit</td>
      </tr>
  </tbody>
</table>
<p><strong>判讀</strong>：GDPR multi-region 成本 1.5-2.5x、但合規是 <em>必要 spend</em>、用 cost optimization 的框架看會誤判；多數歐洲業務 7+ 年回本（避免 4% revenue fine）。</p>
<h2 id="整合--下一步">整合 / 下一步</h2>
<h3 id="跟-postgresql--aurora-對位">跟 <a href="/blog/backend/01-database/vendors/postgresql/migrate-to-aurora/" data-link-title="PostgreSQL → Aurora Migration：protocol 相容、operational 重設計" data-link-desc="Aurora 號稱 PostgreSQL-compatible 但 operational model 不同（storage decouple / cluster endpoint / instance class / 自家備份）；遷移流程是混合（protocol drop-in &#43; operational phased）、5 個 production 踩雷（extension 不支援 / replication slot 不直通 / autovacuum 行為差 / IAM 認證強制 / cost model 換算）、跟 Patroni / read replica / DR 對位">PostgreSQL → Aurora</a> 對位</h3>
<p>Aurora Global Database 可簡化跨 region setup、但 residency filter 仍需 application 端；不是「Aurora 就解決 GDPR」。</p>
<h3 id="跟-multi-dc-mongodb-對位">跟 <a href="/blog/backend/01-database/vendors/mongodb/shard-expansion-multi-dc/" data-link-title="MongoDB Shard Expansion &#43; Multi-DC：Type F「不需要 parallel run」的 multi-region 例外" data-link-desc="MongoDB sharded cluster 加 shard &#43; 跨 DC expansion 是 Type F「topology re-layout」第 3 個 dogfood — 同時改 sharding &#43; replication topology &#43; region distribution；驗證 [#128](/report/data-topology-as-audit-dimension/) self-aware limitation 第 3 點「Type F 不需要 parallel run」claim 的例外（multi-region rollout 必須 parallel run &#43; 切流量）；涵蓋 chunk migration / replica set add member / cross-DC routing">Multi-DC MongoDB</a> 對位</h3>
<p>兩篇都是 multi-region rollout、但本文加合規維度；MongoDB 篇純 capacity + DR driver、本文加 residency constraint、結構不同。</p>
<h3 id="跟-128-self-aware-limitation-第-1-點對位">跟 #128 self-aware limitation 第 1 點對位</h3>
<p>本文驗證 <em>residency axis 候選</em>：</p>
<ul>
<li><strong>Yes 軸獨立</strong>：reverse-constrain topology + operational + application、且帶獨立 compliance 工作量（DPIA / evidence collection / DPO sign-off）</li>
<li><strong>作為 driver 不夠</strong>：methodology 把 residency 歸為 driver 太窄、忽略 cross-cutting constraint 性質</li>
</ul>
<p>未來 audit 可能擴 7 維（加 residency / compliance contract）；累積 PCI / HIPAA / SOX 等不同合規 case 後再評估。</p>
<h3 id="下一步議題">下一步議題</h3>
<ul>
<li><strong>Identity + Consistency + Residency 三軸候選統合</strong>：本批 3 篇分別驗證、未來累積 evidence 後考慮獨立 #129 卡 / 擴 audit 到 7-8 維</li>
<li><strong>Schrems II + new EU data transfer rules</strong>：跨大西洋資料傳輸法規變動快、playbook 半衰期短</li>
<li><strong>Data localization in China / Russia / India</strong>：類似 GDPR 但細節不同、未來 case 累積後評估</li>
</ul>
<h2 id="相關連結">相關連結</h2>
<ul>
<li>上游 vendor 頁：<a href="/blog/backend/01-database/vendors/postgresql/" data-link-title="PostgreSQL" data-link-desc="多用途 OLTP 主流關聯式資料庫、MVCC、豐富 SQL 特性、是 Aurora / Cosmos DB / Spanner / CockroachDB / Aurora DSQL 的相容目標">PostgreSQL</a></li>
<li>平行 multi-region case：<a href="/blog/backend/01-database/vendors/mongodb/shard-expansion-multi-dc/" data-link-title="MongoDB Shard Expansion &#43; Multi-DC：Type F「不需要 parallel run」的 multi-region 例外" data-link-desc="MongoDB sharded cluster 加 shard &#43; 跨 DC expansion 是 Type F「topology re-layout」第 3 個 dogfood — 同時改 sharding &#43; replication topology &#43; region distribution；驗證 [#128](/report/data-topology-as-audit-dimension/) self-aware limitation 第 3 點「Type F 不需要 parallel run」claim 的例外（multi-region rollout 必須 parallel run &#43; 切流量）；涵蓋 chunk migration / replica set add member / cross-DC routing">MongoDB Shard + Multi-DC</a></li>
<li>平行 axis 候選驗證：<a href="/blog/backend/07-security-data-protection/vendors/hashicorp-vault/migrate-to-aws-secrets-manager/" data-link-title="Vault → AWS Secrets Manager：「secret」不是「secret」、identity model 才是核心差異" data-link-desc="Vault → AWS Secrets Manager migration 表面是 secret store 替換、實際核心是 identity model 對位（Vault token &#43; policy vs AWS IAM &#43; resource policy）；驗證 [#128](/report/data-topology-as-audit-dimension/) self-aware limitation 提出的 identity axis 候選 — identity 是否獨立 audit 軸；5 個 production 踩雷（IAM principal 對位 / dynamic credential 對等失敗 / lease lifecycle 模型不同 / audit log 結構差 / 計費模型反轉）">Vault → AWS Secrets Manager</a>（identity 候選）/ <a href="/blog/backend/01-database/vendors/dynamodb/consistency-model-optimization/" data-link-title="DynamoDB Strongly Consistent → Eventually Consistent：same protocol, different contract" data-link-desc="DynamoDB consistency model 從 strongly consistent read 改 eventually consistent read 是 50% cost 優化但風險集中在 application contract — 同 vendor / 同 protocol / 同 table / 不同 read consistency；驗證 [#128](/report/data-topology-as-audit-dimension/) self-aware limitation 提出的 consistency axis 候選；涵蓋 read pattern audit / 5 個 production 踩雷">DynamoDB Consistency Model</a>（consistency 候選）</li>
<li>Methodology：<a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">Migration playbook methodology</a> / <a href="/blog/report/data-topology-as-audit-dimension/" data-link-title="Data topology 是 process content 的第 6 audit 維度" data-link-desc="Process content 的 diff dimension audit 原本 5 維（schema / operational / paradigm / components / application change）漏了 *data topology* — 資料在 cluster / partition / region 之間的分佈拓樸；topology 不在既有 5 維任一個、但決定 re-sharding / partition redesign / multi-region rollout 的結構；本卡擴 audit 到 6 維、新增 Type F「Topology re-layout」結構">#128 self-aware limitation 第 1 點</a>（residency axis 候選驗證、本文是該驗證的 dogfood）</li>
</ul>
]]></content:encoded></item></channel></rss>