# 從裝置出發打造通訊軟體：local-first、端對端加密與端上搜尋

> 通訊軟體改以裝置上的資料為準、伺服器只負責轉送之後，身分金鑰、加密、排序、多裝置歷史和搜尋都要重新安排。用 local-first 的七個理想對照 Signal、WhatsApp、LINE 的公開設計。

原文：https://sheng.page/posts/local-first-messaging/ · 發布：2026-10-06 · 作者：Sheng

常見的通訊軟體系統設計預設伺服器擁有聊天紀錄。改成以裝置上的資料為準、伺服器只負責轉送，整份設計要回答的問題就換了一組：身分和金鑰放哪、伺服器看得到什麼、訊息怎麼排序、多台裝置怎麼對齊歷史、搜尋在哪裡做。這篇逐一走過，每一段都對照現有產品公開過的設計。

「設計一個通訊軟體」是 system design 的老題目，常見答案照同一份目錄走：DynamoDB 的 GSI 怎麼切、Kafka 跟 Redis PubSub 選哪個、ZooKeeper 管連線、consistent hashing 找 chat server、每台裝置一個 device ID、靠 sequence number 做多裝置同步。Wondering 平台上一門系統設計課的大綱就是這樣排的。

## 先決定資料以誰為準

Local-first 這個詞來自 Ink & Switch 2019 年的論文《Local-first software: You own your data, in spite of the cloud》，作者是 Martin Kleppmann 等四人，發表在 Onward! 2019。論文列了七個理想：快、多裝置、離線、協作、長期可用、隱私、使用者掌控。

把它們當成通訊軟體的需求清單，每一項會對應到一個設計決定：

| 理想 | 對通訊軟體的要求 |
|---|---|
| 快（不等網路來回） | 送出先寫本地資料庫，畫面不等伺服器回應 |
| 多裝置 | 每台裝置各存一份完整紀錄 |
| 離線 | 離線可讀可寫，恢復連線後補送 |
| 協作 | 群組裡多人同時發言，合併不需要中央協調 |
| 長期可用 | 服務停了，本地紀錄仍可讀、可匯出 |
| 隱私 | 端對端加密，伺服器看不到內容 |
| 使用者掌控 | 資料格式公開，使用者帶得走 |

最難的是長期可用和使用者掌控。本地紀錄留得住，但只要身分（電話號碼或帳號）和轉送還靠中央伺服器，服務一停就無法再收發，資料格式也由廠商決定。完全去中心化是另一個量級的題目，這篇預設保留一台轉送用的伺服器，其餘盡量放回裝置。

## 身分與裝置

資料以裝置為準，第一個要定的是身分怎麼認。做法是每台裝置產生自己的 identity key，私鑰不離開裝置；伺服器只維護一份目錄，記錄帳號底下有哪些裝置、各自的公鑰。發訊息時由發送端依收發雙方的裝置清單，各加密一份送出（client-fanout）。

WhatsApp 2021 年公開的多裝置設計是現成的例子。舊架構裡手機是唯一的資料來源，網頁版和桌面版透過一條持續連線鏡像手機畫面，手機斷線，其他裝置就跟著停。新架構讓每台裝置各有 identity key，發送端加密 N 份給 N 台裝置，伺服器只保留帳號對裝置的對照表。

這個架構下，伺服器手上最敏感的東西是公鑰目錄。公鑰由伺服器發，如果它偷換成自己的，端對端加密就失效了。傳統做法是雙方當面掃 QR code 或比對安全碼，但 Meta 算過，100 人的群組要兩兩驗證 4,950 次。WhatsApp 在 2023 年 4 月 13 日公開 key transparency：伺服器維護一份只增不改的 Auditable Key Directory，每次變動都寫進公開的稽核紀錄，用戶端可以自己檢查聯絡人的公鑰是否跟別人看到的一致。實作 `akd` 以 Rust 開源。

## 伺服器看得到什麼

伺服器看不到內容，靠的是端對端加密（E2EE）。各家都說自己有加密，實際保證差很多，先把幾個常混用的詞分開。

### 傳輸加密與端對端加密

傳輸加密（TLS 之類）保護手機到伺服器這一段，伺服器收到後解開、存起來，看得到明文。端對端加密的金鑰只在收發雙方的裝置上，伺服器轉送的是它解不開的密文。Telegram 的一般聊天（cloud chats）屬於前者，訊息存在伺服器、預設沒有 E2EE；只有一對一的 secret chats 是端對端加密，而且綁在雙方各自建立對話的那台裝置上，換裝置就看不到。Durov 替 cloud chats 的設計辯護時說，存在伺服器可以避開第三方不安全的備份，也讓使用者從任何裝置存取訊息。

### Forward secrecy

今天的金鑰外洩，不會連帶解開過去的訊息。做法是用短期的 session 金鑰，用完即丟，現在的金鑰推導不回過去的金鑰。Double Ratchet 更進一步，每則訊息換一把。

### Post-compromise security

方向相反的保證：裝置被入侵、金鑰外洩之後，只要雙方再做一次新的金鑰交換，之後的訊息又回到安全狀態，也有人叫 future secrecy。Double Ratchet 規格把兩者分給不同元件：對稱金鑰 ratchet 讓過去的金鑰在攻擊者眼中像亂數，但輸入固定，無法從入侵中恢復；Diffie-Hellman ratchet 每輪帶進新的金鑰對，補上恢復能力。

### Metadata

E2EE 保護內容，不保護誰在什麼時候跟誰講話。維基百科對 Signal Protocol 的描述直接寫它「不提供匿名性」。前面那份帳號對裝置的目錄，就是伺服器一定會知道的 metadata。

### 後量子

威脅模型是「現在先錄下密文，等量子電腦成熟再解」。各家的進度：

- Signal 2023 年推出 PQXDH，把初次金鑰交換換成後量子版本。
- Apple 2024 年 2 月 21 日發表 iMessage PQ3，iOS 17.4 上線。除了初次金鑰交換，對話中也定期用 Kyber-768 重新協商（大約每 50 則訊息，至少每 7 天一次）。Apple 把各家分成 Level 0 到 3，PQ3 放在 Level 3，這是廠商自評。
- Signal 2025 年 10 月 2 日發表 Triple Ratchet：原本的 Double Ratchet 旁邊加一條 Sparse Post-Quantum Ratchet（SPQR），用 ML-KEM 768，兩條 ratchet 的輸出一起混進訊息金鑰。ML-KEM 的 encapsulation key 1,184 bytes、ciphertext 1,088 bytes，ECDH 只要 32 bytes，所以 SPQR 用 erasure coding 把大金鑰切成小塊，分散夾帶在多則訊息裡。

### 群組

群組的成本比一對一高得多。最直觀的是 pairwise fan-out，每個成員各加密一份，前面身分一節的 client-fanout 就是這個思路，成員和裝置一多就線性變貴。另一種是 sender key，每個發送者一把群組金鑰，加密一次大家共用。RFC 9420 的說法是它能做到 forward secrecy，但要達成 post-compromise security，更新成本會隨群組人數的平方成長。

Matrix 的 Megolm 屬於 sender key 這一類：發送端建立一個 outbound session，透過一對一的 Olm 通道把金鑰交給房間裡的每台裝置，之後每送一則就把金鑰做一次雜湊往前推，拿到目前狀態的人讀得到之後的訊息，讀不到之前的。IETF 在 2023 年 7 月發布 Messaging Layer Security（MLS，RFC 9420），用樹狀結構做金鑰封裝，成員推導與更新共用金鑰的成本跟群組人數的對數成正比，適用範圍寫的是「兩人到數千人」。

## 現有產品怎麼選

協定和性質講完，對照現有產品的公開資訊：

| 軟體 | 協定 | 預設 E2EE | 備註 |
|---|---|---|---|
| Signal | Signal Protocol（PQXDH、Triple Ratchet） | 是 | SPQR 逐步上線，完整強制需要後續更新 |
| WhatsApp | Signal Protocol | 是，2016-04-05 起 | 2023 年加入 key transparency；雲端備份的 E2EE 要自己開（2021-10-14 起） |
| Messenger | Signal Protocol 加 Labyrinth | 個人訊息與通話，2023-12-06 起分批上線 | 歷史訊息加密後存在 Meta 伺服器 |
| iMessage | PQ3 | 是 | iOS 17.4 起 |
| RCS（Google Messages、iPhone） | Google Messages 一對一用 Signal Protocol（2020-11 起）；跨平台依 GSMA Universal Profile 3.0 採 MLS | iPhone 端 iOS 26.5 預設開啟，2026-05-11 起以 beta 分批上線 | 需電信商支援；Apple 新聞稿沒寫協定名稱，MLS 出自 GSMA 對 Universal Profile 3.0 的說明 |
| LINE | Letter Sealing v2（ECDH、AES-256-GCM） | 是，2016 年主要用戶端預設開啟，2021 年起無法關閉 | 一對一、50 人以下群組、一對一通話；Open Chat、官方帳號、群組通話不在範圍內，聊天室加入 bot 後也會失去保護 |
| Telegram | MTProto 2.0 | 否 | 只有一對一 secret chats 有 E2EE，群組沒有 |
| Matrix（Element 等） | Olm／Megolm（vodozemac） | 私人對話，2020-05 起 | 規劃以 MSC2883 改用 MLS 做群組加密 |

表上沒列的 Instagram 是反例。Meta 在 2026 年 3 月宣布，Instagram 私訊的端對端加密在 2026 年 5 月 8 日停止支援，給的理由是只有一小部分使用者開啟。這項功能在 Instagram 上一直是選用，想保留加密對話的使用者被引導到預設開啟 E2EE 的 WhatsApp。同一家公司旗下，WhatsApp 和 Messenger 都預設開啟，Instagram 則一直沒有。

LINE 值得多講一點。依 LINE-Break 在 Black Hat 的簡報，台灣約有 2,100 萬人使用，約占人口九成。2017 年多倫多大學 Citizen Lab 與新墨西哥大學的研究者在 FOCI 發表分析，檢驗當時的 6.7.1 版，找到兩個問題：MAC 沒有涵蓋來源、目的地與序號等 metadata，可以重放錄下的訊息；forward secrecy 只做到用戶端到伺服器這段，用戶端之間沒有。LINE 回應時承認這兩點，對後者的理由是要和次要裝置同步。LINE 2021 年的加密白皮書 v2.1 也只把 forward secrecy 寫在用戶端與伺服器之間的傳輸層。

2025 年，丹麥 Aarhus 大學的 Diego F. Aranha 等人發表 LINE-Break（Black Hat Europe 2025，論文收錄於 ASIACCS 2026），對象換成 Letter Sealing v2。他們的結論是金鑰沒有持續輪換，設計上就沒有 forward secrecy；訊息可以被重放、重排或丟棄，兩端都不會察覺；惡意使用者和攻擊者串通時可以冒用發訊者；貼圖與網址預覽洩漏的明文比官方文件寫的多。研究者在 2025 年 6 月 6 日通報，LY 確認了這些發現，回應提到當初為了使用體驗，改以伺服器收到訊息的時間排序，並表示正在研究更新加密協定。

## 訊息排序：sequence number 要誰來發

常見答案裡的 sequence number，通常由伺服器在訊息進來時遞增指派。好處是全域有序，每台裝置拿到的順序一致；代價是沒有伺服器就排不出順序，離線寫入只能先掛著。前面的 LINE 正是這種做法。

Local-first 的做法是讓每台裝置各自寫，事後合併。合併函數只要滿足交換律、結合律、冪等，任何順序收到、收到幾次，最後狀態都一樣，這就是 state-based CRDT 的條件（Shapiro 等人 2011 年正式定義）。聊天紀錄是最容易套用的結構，先假設訊息只增不改（不處理編輯和收回），合併就是取聯集，再用一個確定的 key 排序。

下面用 Lamport clock 加 device ID 當排序 key，模擬兩台裝置各自離線寫兩則，再互相合併：

```python
from dataclasses import dataclass
from importlib.metadata import version
import platform
import tempfile

from fastembed import TextEmbedding
from qdrant_client import QdrantClient, models


@dataclass(slots=True, frozen=True)
class Message:
    lamport: int
    device: str
    text: str

    @property
    def key(self) -> tuple[int, str]:
        return (self.lamport, self.device)


class DeviceLog:
    def __init__(self, device: str) -> None:
        self.device = device
        self.clock = 0
        self.messages: dict[tuple[int, str], Message] = {}

    def write(self, text: str) -> None:
        self.clock += 1
        msg = Message(self.clock, self.device, text)
        self.messages[msg.key] = msg

    def merge(self, other: "DeviceLog") -> None:
        # Union of logs: commutative, associative, idempotent.
        self.messages.update(other.messages)
        self.clock = max(self.clock, other.clock)

    def ordered(self) -> list[Message]:
        return sorted(self.messages.values(), key=lambda m: m.key)


phone, laptop = DeviceLog("phone"), DeviceLog("laptop")
phone.write("週六爬山，記得帶雨衣")
phone.write("登山口集合時間改成早上七點")
laptop.write("報稅截止日是五月底")
laptop.write("這次機票訂桃園出發的早班機")

phone.merge(laptop)
laptop.merge(phone)
assert phone.ordered() == laptop.ordered()
for m in phone.ordered():
    print(m.lamport, m.device, m.text)
```

輸出：

```text
1 laptop 報稅截止日是五月底
1 phone 週六爬山，記得帶雨衣
2 laptop 這次機票訂桃園出發的早班機
2 phone 登山口集合時間改成早上七點
```

兩邊順序一致，assert 通過。但 laptop 和 phone 的訊息交錯排列，跟實際寫入的時間無關。Lamport clock 給出跟因果一致的全序，但不反映實際時間，對聊天來說，離線期間兩邊各自講的話，合併後會互相穿插。由伺服器發 sequence number 可以避開這種怪異感，代價是離線時排不出順序。排序要交給伺服器還是留在裝置，看產品對離線寫入的需求有多重。

## 多裝置的歷史紀錄

加密和排序都定了，接下來是新裝置加入時，舊訊息從哪來。現有產品在這裡的選擇各不相同：

- Telegram 的一般聊天讓伺服器以它解得開的形式保存，換來任何裝置隨時登入都看得到全部歷史。
- LINE 以次要裝置同步為由，放棄用戶端之間的 forward secrecy（見前文）。
- WhatsApp 讓主裝置把近期聊天加密打包，直接傳給新裝置，資料留在使用者的裝置之間。
- Messenger 用 Labyrinth 把加密後的歷史存在 Meta 伺服器上，金鑰由使用者控制。Meta 列出的難題之一，就是讓使用者不必依賴本地儲存也讀得到歷史。

Forward secrecy 要求舊金鑰用完即丟，歷史紀錄卻要求新裝置事後還能讀到舊訊息，兩件事必須有一邊讓步：舊訊息在某處用一把長期金鑰重新加密保存，或者只能從另一台還有明文的裝置拿。WhatsApp 的裝置連結和 local-first 選後者，以裝置上的資料為準；Messenger 選前者，伺服器只保存解不開的密文。WhatsApp 的雲端備份也屬於前者，只是預設沒有端對端加密，要使用者自己開。

存在伺服器的那份還有法規風險。2025 年 2 月，據報英國政府依《調查權力法》要求 Apple 提供存取 iCloud 加密資料的能力，Apple 隨後停止對英國使用者提供 Advanced Data Protection，既有使用者得在期限內關閉，iCloud 備份等資料回到 Apple 持有金鑰的標準保護。依 The Register 2025 年 2 月 24 日的報導，iMessage 本身的端對端加密不受影響。

從 local-first 的角度看，E2EE 對應的是七個理想裡的隱私，副作用是伺服器能替你做的事變少：它解不開，就沒辦法替你搜尋、合併、建索引，這些工作全落到裝置上。

## 搜尋只能在裝置上做

本地資料庫做關鍵字比對沒問題，想做語意搜尋（「上次說幾點集合？」）就需要在裝置上放向量索引。Qdrant 有兩條路：

- `qdrant-client` 的 local mode：`QdrantClient(path=...)`，在同一個 process 裡跑，資料存在指定資料夾，不需要另外起服務。README 寫的用途是開發、原型和測試。
- Qdrant Edge：2025 年 7 月 29 日發表，定位是給機器人、手機、POS、IoT 用的嵌入式向量搜尋，以 library 形式執行，沒有背景 optimizer 或 update thread，所有操作同步、由應用程式控制。發表時是 private beta，目前官方文件標示為 beta，並註明 API 與功能之後可能變動；Python 套件是 `qdrant-edge-py`，另有 Rust crate `qdrant-edge`。

接著排序一節的程式，把合併後的訊息用多語 embedding 模型轉成向量，丟進 local mode 搜尋：

```python
# Pin the model name; every device must embed with the same model and version.
MODEL = "sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
embedder = TextEmbedding(MODEL)
docs = phone.ordered()
vectors = list(embedder.embed([m.text for m in docs]))

with tempfile.TemporaryDirectory() as path:
    client = QdrantClient(path=path)  # in-process, data stays in this folder
    client.create_collection(
        "chat",
        vectors_config=models.VectorParams(
            size=len(vectors[0]), distance=models.Distance.COSINE
        ),
    )
    client.upsert(
        "chat",
        points=[
            models.PointStruct(
                id=i,
                vector=v.tolist(),
                payload={"text": m.text, "device": m.device, "lamport": m.lamport},
            )
            for i, (m, v) in enumerate(zip(docs, vectors))
        ],
    )
    query = next(iter(embedder.embed(["上次說幾點集合？"]))).tolist()
    hits = client.query_points("chat", query=query, limit=2).points
    for h in hits:
        print(f"{h.score:.3f}", h.payload["text"])
    client.close()

print("Python", platform.python_version())
print("qdrant-client", version("qdrant-client"), "fastembed", version("fastembed"))
```

輸出：

```text
0.538 登山口集合時間改成早上七點
0.390 週六爬山，記得帶雨衣
Python 3.13.12
qdrant-client 1.19.1 fastembed 0.8.1
```

模型一開始用的是較小的 `paraphrase-multilingual-MiniLM-L12-v2`（fastembed 標示約 0.22 GB），同一個問題排第一的是「報稅截止日是五月底」（0.404），正確答案排第二（0.322）；換成上面約 1 GB 的 `paraphrase-multilingual-mpnet-base-v2` 才排對。四則訊息說明不了模型好壞。模型要拿自己的資料評估，放到手機上還得在檔案大小和準確度之間取捨。

多裝置各自建索引時，embedding 必須在每台裝置上算出一樣的結果。模型名稱、版本、tokenizer、正規化方式都要鎖住，否則同一則訊息可能在一台搜得到、另一台搜不到。自己寫特徵時也一樣，雜湊要用 `hashlib`，Python 內建的 `hash()` 每個 process 的 salt 不同，同一句話在手機和筆電上會算出不同結果。

另一個要決定的是索引同不同步。向量索引是可以從訊息重建的衍生資料，比較簡單的做法是只同步訊息 log，各裝置自己建索引。代價是新裝置連結時要重算一輪，手機上的耗電和時間得量過才知道。

## 伺服器剩下什麼工作

照這個方向設計，伺服器的職責縮成幾件：

- 裝置目錄：帳號對應哪些裝置、各自的公鑰，加上供用戶端查核的 key transparency 稽核紀錄。
- 信箱：收件裝置離線時暫存加密訊息，送達後刪除。
- 推播喚醒。

開頭那份常見答案裡的 DynamoDB GSI、ZooKeeper、consistent hashing 還用得上，只是服務的對象從聊天紀錄變成待送的加密封包。資料量和保存期限都小很多，設計重點從查詢模式移到連線管理和送達保證。

伺服器省下的複雜度會搬到用戶端：合併邏輯、本地 schema migration、端上索引、裝置遺失後的復原。

## 補充筆記

- 常見的通訊軟體設計預設伺服器擁有聊天紀錄；改成以裝置為準，伺服器剩下裝置目錄、離線信箱和推播。
- 每台裝置一把 identity key，伺服器保管公鑰目錄，所以需要 key transparency 讓用戶端查核目錄有沒有被偷換。
- 傳輸加密、E2EE、forward secrecy、post-compromise security 是不同的保證；LINE-Break 指出 Letter Sealing v2 設計上沒有 forward secrecy。
- 群組加密的成本差很多：pairwise 線性、sender key 做 post-compromise security 時平方、MLS 對數。
- Lamport clock 加 device ID 讓多裝置合併後順序一致，但順序跟實際時間無關。
- 伺服器解不開的資料只能在裝置上搜尋，embedding 模型要鎖版本，也要拿自己的資料評估。

## 延伸閱讀

- Wondering〈[How to Design WhatsApp](https://wondering.app/learn/how-to-design-whatsapp-00778c2bcdcf373ebc442c10)〉：系統設計課程，本文只引用公開大綱，當作常見答案的例子。
- Ink & Switch〈[Local-first software: You own your data, in spite of the cloud](https://www.inkandswitch.com/local-first/)〉：2019 年原始論文，七個理想出自這裡。
- Meta Engineering〈[How WhatsApp enables multi-device capability](https://engineering.fb.com/2021/07/14/security/whatsapp-multi-device/)〉：2021 年多裝置架構的一手說明。
- Meta Engineering〈[Deploying key transparency at WhatsApp](https://engineering.fb.com/2023/04/13/security/whatsapp-key-transparency/)〉：2023-04-13，AKD 的設計與 4,950 次驗證的例子。
- Meta Engineering〈[Building end-to-end security for Messenger](https://engineering.fb.com/2023/12/06/security/building-end-to-end-security-for-messenger/)〉：2023-12-06，Labyrinth 與多裝置歷史的設計說明；預設開啟的公告見〈[Launching Default End-to-End Encryption on Messenger](https://about.fb.com/news/2023/12/default-end-to-end-encryption-on-messenger/)〉。
- Help Net Security〈[Meta ditches end-to-end encrypted messaging on Instagram](https://www.helpnetsecurity.com/2026/03/16/instagram-end-to-end-encrypted-messaging-ending/)〉：2026-03-16，Instagram 停止 E2EE 的日期與 Meta 的理由，屬新聞報導。
- Signal〈[The Double Ratchet Algorithm](https://signal.org/docs/specifications/doubleratchet/)〉：官方規格，forward secrecy 與 break-in recovery 的分工出自這裡。
- Signal〈[Signal Protocol and Post-Quantum Ratchets](https://signal.org/blog/spqr/)〉：2025-10-02，SPQR 與 Triple Ratchet 的一手說明。
- Apple Security Research〈[iMessage with PQ3](https://security.apple.com/blog/imessage-pq3/)〉：2024-02-21，Level 分級為 Apple 自評。
- IETF〈[RFC 9420: The Messaging Layer Security (MLS) Protocol](https://www.rfc-editor.org/rfc/rfc9420.html)〉：標準本文，三種群組加密方式的成本比較出自 Introduction。
- Matrix.org〈[End-to-End Encryption implementation guide](https://matrix.org/docs/matrix-concepts/end-to-end-encryption/)〉：Megolm 的 outbound session 與金鑰分送方式，官方文件。
- Privacy Guides〈[Apple Introduces End-to-End Encrypted RCS Messaging in the iOS 26.4 Beta](https://www.privacyguides.org/news/2026/02/19/apple-introduces-end-to-end-encrypted-rcs-messaging-in-the-ios-26-4-beta/)〉：2026-02-19，iOS 26.4 beta 首次出現時的報導，引用 GSMA 對 Universal Profile 3.0 採用 MLS 的說明，二手整理。
- Apple〈[End-to-end encrypted RCS messaging begins rolling out today in beta](https://www.apple.com/newsroom/2026/05/end-to-end-encrypted-rcs-messaging-begins-rolling-out-today-in-beta/)〉：2026-05-11 新聞稿，沒寫明使用的協定。
- Espinoza 等〈[Analysis of End-to-End Encryption in the LINE Messaging Application](https://www.usenix.org/system/files/conference/foci17/foci17-paper-espinoza.pdf)〉：FOCI 2017，同儕審查的學術分析，對象是當年版本。
- Aranha、Hansen、Mogensen〈[LINE-Break: Cryptanalysis and Reverse Engineering of Letter Sealing](https://linebreak.info/)〉：Aarhus 大學，ASIACCS 2026 論文；台灣使用人數、forward secrecy 結論與 LY 回應見 [Black Hat Europe 2025 簡報](https://i.blackhat.com/BH-EU-25/BHEU25-Mogensen-LINE-Break-Cryptanalysis-And-Reverse-Engineering-Of-Letter-Sealing-Final.pdf)。
- LINE〈[LINE Encryption Overview Technical Whitepaper v2.1](https://scdn.line-apps.com/stf/linecorp/en/csr/line-encryption-whitepaper-ver2.1.pdf)〉：2021 年 11 月官方白皮書，屬廠商自述；加密報告最新版見〈[LINE Encryption Report (2025)](https://www.lycorp.co.jp/en/privacy-security/security/transparency/encryption-report/2025/)〉。
- The Register〈[Apple ends iCloud Advanced Data Protection for UK customers](https://www.theregister.com/security/2025/02/24/apple-ends-icloud-advanced-data-protection-for-uk-customers/588745)〉：2025-02-24，英國 ADP 停用的經過，屬新聞報導。
- Qdrant〈[Qdrant Edge: Vector Search for Embedded AI](https://qdrant.tech/blog/qdrant-edge/)〉：2025-07-29 發表文；目前狀態見〈[Qdrant Edge 文件](https://qdrant.tech/documentation/edge/)〉，皆屬廠商自述。
- Wikipedia〈[Signal Protocol](https://en.wikipedia.org/wiki/Signal_Protocol)〉、〈[Telegram (software)](https://en.wikipedia.org/wiki/Telegram_(software))〉、〈[Matrix (protocol)](https://en.wikipedia.org/wiki/Matrix_(protocol))〉、〈[Conflict-free replicated data type](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)〉：協定組成、時間線、MSC2883 與 CRDT 定義，二手整理，原始出處見各條目註腳。

> 想法與技術判斷出自 Sheng，和 Claude 一起起草 · 範例在 Python 3.13.12、qdrant-client 1.19.1、fastembed 0.8.1 實測