DLP 是一个基于 Shamir 秘密分割和 Ed25519 签名的去中心化数字遗产继承协议,用受托人仲裁机制替代平台客服,解决用户离世后数字资产无法安全传递的问题。
# Digital Legacy Protocol (DLP)
[](https://github.com/Ciprian-LocalPulse/digital-legacy-protocol/actions/workflows/ci.yml)
[](LICENSE)
[](spec/SPEC.md)
[](pyproject.toml)
[](WHITEPAPER.md)
**一个开放协议,用于处理你离开人世后的数字资产——由你信任的人而非公司的工单系统来验证。**
价值数十亿美元的加密货币因私钥随持有者离世而永久丢失。家庭为了关闭已故亲人的账户,往往要花数月时间与平台客服抗争。每种服务——银行、交易所、电子邮件提供商、密码管理器——都为此制定了各自不兼容且通常根本不存在的政策。DLP 试图用一个标准来代替某家公司的私有方案,以解决这一问题。
没有需要注册的服务器,没有 token,没有订阅。它只是一个规范加上一个参考实现。你可以 fork 它、嵌入它,或者无视它。
如需正式的详细说明——动机、相关工作、威胁模型以及对尚未证实部分的明确说明——请参阅 [WHITEPAPER.md](WHITEPAPER.md)。
## 核心概念概述
你编写并签署一份 manifest:*“如果我失踪了,请让我女儿访问这个钱包,删除这个账户,传递这条信息。”* 真正的机密(私钥、密码)从不以完整形式存储在任何地方——它们会通过 Shamir's Secret Sharing 在你指定的受托人之间进行拆分,因此没有任何单一的受托人、平台或攻击者能够单独采取行动。如果你在足够长的时间内没有签到,你的每位受托人都会被问到一个问题:*“Stefano 是真的不在了,还是他只是忘了?”* 只有当足够多的人达成一致时,信息才会被释放——如果你只是在一艘没有信号的船上,他们中的任何一个人都可以终止流程。
## 为什么目前还没有这样的方案
这并不是一个复杂的密码学问题——Shamir's Secret Sharing 已经存在几十年了,Ed25519 签名也早已是成熟技术。这是一个协调问题:没有哪个平台愿意开发“用户离世后的处理”功能,因为这很沉重、无利可图,而且也没有其他平台支持,因此根本没有可以*兼容*的对象。DLP 试图成为大家都可以兼容的标准,并将其作为公共领域发布,这样就没有人会有理由拒绝采纳它。
## 快速开始
```
pip install -e .
dlp demo
```
这将端到端运行一个完整的场景:为所有者和三位受托人生成密钥,构建并签署一个 2-of-3 的 manifest,拆分一个演示密钥,模拟 200 天没有签到的情形,收集两位受托人的证明,恢复密钥,并将其交给一个演示平台 adapter。整个过程不涉及网络——全部都在本地运行,因此你可以结合输出结果阅读 `dlp/cli.py` 中的 `cmd_demo` 函数,清楚地了解每一步到底发生了什么。
## 作为库使用
```
from dlp import ManifestBuilder, crypto, shamir
# 为 owner 和三个 trustees 生成密钥
owner_priv, owner_pub = crypto.generate_keypair()
t1_priv, t1_pub = crypto.generate_keypair()
t2_priv, t2_pub = crypto.generate_keypair()
t3_priv, t3_pub = crypto.generate_keypair()
# 构建 2-of-3 manifest
manifest = (
ManifestBuilder(owner_public_key=owner_pub, owner_display_name="Stefano")
.add_trustee("t1", t1_pub, contact_hint="brother")
.add_trustee("t2", t2_pub, contact_hint="close friend")
.add_trustee("t3", t3_pub, contact_hint="lawyer")
.set_quorum_threshold(2)
.add_beneficiary("daughter", contact_hint="my daughter")
.add_asset(
asset_type="crypto_wallet",
reference="cold storage wallet #1",
beneficiary_id="daughter",
action="release_key",
shares_distributed_to=["t1", "t2", "t3"],
)
.build_and_sign(owner_priv)
)
# 真正的 secret 绝不会存在于 manifest 中 — 它会被单独分割
private_key_material = b"...actual wallet key..."
shares = shamir.split_secret(private_key_material, threshold=2,
trustee_ids=["t1", "t2", "t3"])
# 将 shares[i] 通过带外方式分发(encrypted email、纸质等)给每个 trustee
```
验证你收到的 manifest:
```
from dlp import validate_manifest, is_signature_valid
validate_manifest(manifest) # raises ManifestValidationError if malformed
is_signature_valid(manifest) # True/False
```
在达到法定人数(quorum)后恢复密钥:
```
from dlp import shamir
secret = shamir.reconstruct_secret([shares[0], shares[1]]) # any 2 of the 3
```
加密联系提示,确保只有指定的受托人才能阅读(参见 [spec 4.1](spec/SPEC.md#41-contact-hint-encryption)):
```
from dlp import hint_crypto
enc_priv, enc_pub = hint_crypto.generate_encryption_keypair() # trustee's own keypair
builder.add_trustee("t1", t1_signing_pub, contact_hint="Ada's sister, Elena",
encryption_public_key=enc_pub)
# 只有 enc_priv 才能从存储的 manifest 中恢复明文 hint
```
在本地持久化 manifest,并为所有者提供一种在丢失时恢复自己密钥的方法:
```
from dlp.storage import LocalFileStore
from dlp import recovery
store = LocalFileStore(".dlp_store")
store.save(manifest)
store.load(manifest["manifest_id"])
# 可选,并且是一个真正的权衡 — 使用前请参阅 spec 第 11 节
backup = recovery.backup_owner_key(owner_priv_raw_bytes, threshold=3,
trustee_ids=["t1", "t2", "t3", "t4"])
```
向受托人发送真实的通知,并启动本地 Web UI:
```
from dlp.notify import SMTPEmailChannel, NotificationService
channel = SMTPEmailChannel(host="smtp.gmail.com", port=587,
username="you@gmail.com", password="app-password",
from_address="you@gmail.com")
NotificationService(channel).send_attestation_request(
"sister@example.com", "Ada's sister", owner_display_name="Ada"
)
```
或者改用 SMS 发送,适合那些不常查看邮件的受托人:
```
from dlp.notify import TwilioSMSChannel, NotificationService
channel = TwilioSMSChannel(account_sid="ACxxxxx", auth_token="your-twilio-token",
from_number="+15551230000")
NotificationService(channel).send_attestation_request(
"+15559998888", "Ada's sister", owner_display_name="Ada"
)
```
```
pip install -e ".[web]"
dlp web # serves a local UI at http://127.0.0.1:5000 — create/inspect/verify without touching Python
```
在达到法定人数激活后,将恢复的密钥交给真实平台——目前支持 GitHub(使用 Gists 传递消息,使用 repo collaborators 进行访问授权):
```
from dlp.adapters.github import GitHubAdapter
adapter = GitHubAdapter(personal_access_token="ghp_your_token_here")
result = adapter.on_activation(manifest, asset_id, reconstructed_secret)
print(result.detail) # e.g. "created private gist: https://gist.github.com/..."
```
请参阅 `examples/github_adapter_demo.py` 以获取可运行的端到端版本(设置 `GITHUB_TOKEN` 以查看其调用真实 API 的情况)。
将数据传递给任何能够接收 HTTP POST 的 endpoint——Zapier、聊天 webhook 或你自己的服务器——并进行签名,以便接收方验证真实性:
```
from dlp.adapters.webhook import WebhookAdapter, verify_webhook_signature
adapter = WebhookAdapter(signing_secret="a-shared-secret-both-sides-know")
result = adapter.on_activation(manifest, asset_id, reconstructed_secret)
print(result.detail) # e.g. "webhook delivered, responded with HTTP 200"
# 在接收端:
# verify_webhook_signature(request_body, request.headers["X-DLP-Signature"], "a-shared-secret-both-sides-know")
```
`examples/webhook_adapter_demo.py` 不需要任何外部账户——它会启动自己的本地接收器,因此运行 `python examples/webhook_adapter_demo.py` 就能看到整个签名传递过程端到端地运行。
针对已存储的 manifest 运行真正的 dead man's switch(死人开关)——通过 CLI 运行,你可以在命令之间设定任意间隔天数,因为状态会持久化到磁盘:
```
dlp switch-init
# starts the check-in clock
dlp switch-status # see current state at any time
dlp switch-checkin # owner proves they're alive, resets the clock
# 一旦 overdue + grace period 已过,trustees 证明:
dlp switch-attest --unreachable
dlp switch-attest --reachable # any single one of these aborts activation
```
同样的生命周期也可以通过 `dlp web` 实现——每个 manifest 页面都会显示实时的 switch 状态,并提供签到和证明的按钮,无需使用 CLI。
在 switch 状态改变时,真正通知受托人和受益人——你可以从 cron 运行此操作,或者从命令行使用 `dlp switch-tick`:
```
from dlp.storage import LocalFileStore, LocalSwitchStore
from dlp.notify import SMTPEmailChannel, NotificationService
from dlp.orchestrator import SwitchMonitor
channel = SMTPEmailChannel(host="smtp.gmail.com", port=587,
username="you@gmail.com", password="app-password",
from_address="you@gmail.com")
monitor = SwitchMonitor(LocalFileStore(".dlp_store"), LocalSwitchStore(".dlp_store/switches"),
NotificationService(channel))
attempts = monitor.tick(manifest_id) # safe to call repeatedly — only sends once per state
for a in attempts:
print(a.kind, "->", a.recipient, "OK" if a.success else a.detail)
```
## 本仓库包含的内容
```
digital-legacy-protocol/
├── spec/SPEC.md the actual protocol — start here if you're implementing DLP elsewhere
├── dlp/
│ ├── manifest.py build, validate, and sign manifests
│ ├── crypto.py Ed25519 signing/verification, canonical JSON serialization
│ ├── shamir.py Shamir's Secret Sharing over GF(256), built from scratch
│ ├── hint_crypto.py X25519 + AES-256-GCM encryption for contact hints
│ ├── recovery.py opt-in Shamir backup of the owner's own signing key
│ ├── storage.py ManifestStore interface + a working local file backend
│ ├── notify.py real SMTP email + Twilio SMS delivery, plus the actual message content for each event
│ ├── orchestrator.py connects switch state transitions to actual notification delivery, idempotently
│ ├── switch.py the dead man's switch state machine (check-ins, trustee attestation, quorum) — now with persistence via storage.LocalSwitchStore
│ ├── adapter.py the DLPAdapter interface platforms implement to become DLP-aware
│ ├── adapters/ real adapter implementations — GitHubAdapter (Gists + repo collaborators) and WebhookAdapter (signed HTTP POST to anywhere)
│ ├── webapp/ minimal Flask UI — create/inspect/verify manifests AND run the switch lifecycle, without a terminal (optional extra)
│ └── cli.py `dlp keygen / enckeygen / demo / verify / inspect / store-* / switch-init / switch-status / switch-checkin / switch-attest / web`
├── tests/ 218 tests, 95% coverage package-wide (100% on crypto, switch, and the webhook adapter)
└── examples/ sample manifests, a worked inheritance scenario, and a live GitHub adapter demo
```
## 设计原则(详见 [spec/SPEC.md](spec/SPEC.md))
1. **没有哪家公司能单方面宣布你已离世。** 这需要由你亲自选出的受托人 quorum 决定。
2. **仅凭 manifest 无法获取任何权限。** 它只描述意图;实际的机密已被拆分,需要 quorum 才能恢复。
3. **平台是自愿加入的——它们并不拥有该标准。** 该规范采用 CC0 协议,属于公共领域。
4. **在最后一刻之前均可撤销。** 随时更新或撤销你的 manifest;最新的签名优先。
5. **最小披露原则。** 受托人只能看到他们自己的份额以及与他们相关的指令。
## 本项目*不是*什么
- 不是一家公司,不是一个保险箱,不负责保管你的资金或机密。
- 不是法律遗嘱的替代品——它是一个技术层,可供遗嘱参考,专门用于处理律师无法在密码学层面进行验证的部分。
## 当前状态——在将其用于任何实际事务之前请务必阅读
当前版本为 v0.4:包含一份规范和一个参考实现,并非最终产品。在此直接说明界限所在:
**稳定且经过测试:**核心密码学功能(Shamir's Secret Sharing、Ed25519 签名、X25519 提示加密)、manifest 验证、dead man's switch 状态机——支持持久化,可通过 CLI 和 Web UI 运行,并通过 `dlp.orchestrator` 在正确的状态转换时自动通知相关人员——本地存储、可选的所有者密钥恢复、真实的邮件和 SMS 发送功能、可用的本地 Web UI,以及两个真实的平台 adapter(GitHub 和通用的签名 webhook,共同涵盖了规范中四种操作里的三种)。包含 218 个测试,覆盖率达到 95%,每次推送都会运行 CI。
**已存在并能与真实外部系统交互,但应用仍处于概念验证阶段:**`dlp.adapters.github.GitHubAdapter` 会实际调用 `api.github.com`——为 `deliver_message` 创建私有 Gists,为 `grant_access` 添加 repo collaborators。`dlp.adapters.webhook.WebhookAdapter` 会向任何能够接收的 endpoint 发送签名的 HTTP POST 以执行 `execute_webhook`。目前还没有任何银行、交易所或密码管理器支持 DLP manifest;这两个 adapter 证明了 `DLPAdapter` 接口确实可以实现,并且适用于不同类型的平台,但这尚不足以证明其在现实世界中被广泛采用。`NotificationChannel` 现在提供两种真实的渠道(通过 Twilio 的邮件和 SMS);推送通知则需要各自的具体实现。
**完全尚未实现:**一个*托管式、多租户*的 Web UI——参考 UI 会在服务器端生成私钥,这在本地单用户场景下没问题,但绝对不适用于共享部署环境(参见规范第 14 节);一份独立的安全审计(这里的所有内容都由其作者本人测试过,这与“由没有任何利益相关者进行审查”的标准不同);以及任何关于受托人 quorum 的证明在任何司法管辖区是否具有作为死亡或无行为能力证据的法律效力的法律意见。最后一点具体来说,不是代码能够解决的问题——它需要一位真正的律师,在具体的司法管辖区内进行判定。
如果你正在考虑将其用于实际应用:该协议、密码学以及传递/UI/adapter 层构成了一个可靠的基础,值得在此基础上进行构建。目前缺失的是除了一个概念验证 adapter 之外的真正第三方采用,以及法律基础——这两者都无法仅靠更多的代码来实现。
## License
- **代码**(`dlp/`、`tests/``、`examples/` 下的所有内容):MIT——详见 [LICENSE](LICENSE)。
- **规范**(`spec/SPEC.md`):CC0 1.0,公共领域。任何人都不应需要获得许可才能实现一个离世通知协议。