Happy Docs 中文文档

create: 2026-07-20
update: 2026-07-20
author: thinkycx
title: 【译】安全与加密
description: Happy Coder 的端到端加密架构详解,包括密钥层级(主密钥→内容密钥对→DEK→实际内容)、会话同步流程、安全属性分析,以及各种泄露场景下的影响范围。
category: translation
tags: happy-coder, translation

安全与加密

Happy Coder 使用端到端加密,即使是我们自己也看不到你的代码。

简要概览

  1. 代码在离开设备前就已加密
  2. 只有你持有密钥 —— 主密钥永远不会离开你的手机
  3. 中继服务器无法读取任何内容 —— 它看到的只是加密数据块
  4. 开源 —— 你可以自己审计代码

加密架构

以下图表详细解释 Happy Coder 加密模型的各层结构。

图例与密钥类型

══════════════════════════════════════════════════════════════════════════════
                         HAPPY CODER ENCRYPTION MODEL
══════════════════════════════════════════════════════════════════════════════


┌──────────────────────────────────────────────────────────────────────────────┐
│                                  LEGEND                                      │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  KEY TYPES                                                                   │
│  ─────────                                                                   │
│  ╔════════╗  Secret key     - Must be protected, enables decryption          │
│  ╚════════╝                                                                  │
│                                                                              │
│  ┌────────┐  Public key     - Safe to share, only enables encryption         │
│  └────────┘                                                                  │
│                                                                              │
│  ┌┄┄┄┄┄┄┄┄┐  Ephemeral key  - Temporary, discarded after use                 │
│  └┄┄┄┄┄┄┄┄┘                                                                  │
│                                                                              │
│  «────────»  Data Encryption Key (DEK) - Symmetric, encrypts actual content  │
│                                                                              │
│  ARROWS                                                                      │
│  ──────                                                                      │
│  ───────>  Key/data movement                                                 │
│  ┄┄┄┄┄┄┄>  Encrypted data                                                    │
│                                                                              │
│  LIFECYCLE                                                                   │
│  ─────────                                                                   │
│  [ONCE/ACCOUNT]   Created once per account, lives forever                    │
│  [ONCE/MACHINE]   Created once per CLI machine                               │
│  [ONCE/SESSION]   Created once per coding session                            │
│  [ONCE/AUTH]      Created for each QR auth, discarded immediately            │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

密钥层级

密钥分为四层,从根密钥逐层派生,最终保护实际内容。

══════════════════════════════════════════════════════════════════════════════
                              KEY HIERARCHY
══════════════════════════════════════════════════════════════════════════════


                              LAYER 1: ROOT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ╔═══════════════════════════════════════════════════════════════════════════╗
  ║  MASTER SECRET  (32 bytes)                                    [MOBILE]    ║
  ╠═══════════════════════════════════════════════════════════════════════════╣
  ║                                                                           ║
  ║  Lifecycle:    [ONCE/ACCOUNT] - created at signup                         ║
  ║  Stored:       Mobile secure storage only                                 ║
  ║  Backup:       XXXXX-XXXXX-XXXXX-... (base32)                             ║
  ║                                                                           ║
  ║  ❌ Never leaves device   ❌ Never sent anywhere                           ║
  ║                                                                           ║
  ╚═══════════════════════════════════════════════════════════════════════════╝
                                      │
                                      │ HKDF derive
                                      ▼

                         LAYER 2: CONTENT KEY PAIR
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ╔══════════════════════════════════════╗    ┌────────────────────────────────┐
  ║  CONTENT SECRET KEY                  ║    │  CONTENT PUBLIC KEY            │
  ║  (32 bytes)                [MOBILE]  ║    │  (32 bytes)        [MOBILE+CLI]│
  ╠══════════════════════════════════════╣    ├────────────────────────────────┤
  ║                                      ║    │                                │
  ║  Lifecycle: [ONCE/ACCOUNT]           ║    │  Lifecycle: [ONCE/ACCOUNT]     │
  ║             derived from master      ║    │             derived from secret│
  ║                                      ║    │                                │
  ║  Purpose:                            ║    │  Purpose:                      │
  ║  • Decrypt DEKs from server          ║    │  • Encrypt DEKs for storage    │
  ║  • Sign messages to CLI              ║    │  • Sent to CLI during auth     │
  ║                                      ║    │                                │
  ║  ❌ Never leaves mobile               ║    │  ✓ Shared with all CLIs        │
  ║                                      ║    │                                │
  ╚══════════════════════════════════════╝    └────────────────────────────────┘
            │                                               │
            │ decrypts                                      │ encrypts
            ▼                                               ▼

                    LAYER 3: DATA ENCRYPTION KEYS (DEKs)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  «═══════════════════════════════»  «═══════════════════════════════»
  ║  SESSION DEK                  ║  ║  MACHINE DEK                  ║
  ║  (32 bytes, random AES-256)   ║  ║  (32 bytes, random AES-256)   ║
  ╠═══════════════════════════════╣  ╠═══════════════════════════════╣
  ║                               ║  ║                               ║
  ║  Lifecycle: [ONCE/SESSION]    ║  ║  Lifecycle: [ONCE/MACHINE]    ║
  ║             random per session║  ║             random per CLI    ║
  ║                               ║  ║                               ║
  ║  Stored: Server (encrypted    ║  ║  Stored: Server (encrypted    ║
  ║          with content pubkey) ║  ║          with content pubkey) ║
  ║                               ║  ║                               ║
  ║  Purpose:                     ║  ║  Purpose:                     ║
  ║  • Encrypt session messages   ║  ║  • Encrypt machine metadata   ║
  ║  • Encrypt session metadata   ║  ║  • Encrypt machine state      ║
  ║                               ║  ║                               ║
  ║  Enables future sharing:      ║  ║                               ║
  ║     Re-encrypt DEK for        ║  ║                               ║
  ║     friend's public key       ║  ║                               ║
  ║                               ║  ║                               ║
  «═══════════════════════════════»  «═══════════════════════════════»
            │                                    │
            │ encrypts                           │ encrypts
            ▼                                    ▼

                         LAYER 4: ACTUAL CONTENT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐  ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
  ┆  Session Content (encrypted)  ┆  ┆  Machine Data (encrypted)     ┆
  ┆  ─────────────────────────    ┆  ┆  ───────────────────────      ┆
  ┆  • Claude messages            ┆  ┆  • Machine name, status       ┆
  ┆  • Tool calls & results       ┆  ┆  • Active session info        ┆
  ┆  • File contents              ┆  ┆  • Machine preferences        ┆
  ┆  • Terminal output            ┆  ┆                               ┆
  ┆  • Session metadata           ┆  ┆                               ┆
  ┆                               ┆  ┆                               ┆
  ┆  Stored on server, encrypted  ┆  ┆  Stored on server, encrypted  ┆
  ┆  with session DEK             ┆  ┆  with machine DEK             ┆
  └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘  └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘

认证完成后的密钥状态

认证后,各方持有的密钥清单——服务器无法解密任何内容。

══════════════════════════════════════════════════════════════════════════════
                      KEY STATE AFTER AUTHENTICATION
══════════════════════════════════════════════════════════════════════════════

     MOBILE                            SERVER                         CLI
        │                                 │                             │
        │                                 │                             │
┌───────┴───────────────────────┐  ┌──────┴──────────────┐  ┌───────────┴───────────────────┐
│                               │  │                     │  │                               │
│  ╔═════════════════════════╗  │  │  Encrypted storage  │  │  ┌───────────────────────┐    │
│  ║  master_secret          ║  │  │  only:              │  │  │  content_public_key   │    │
│  ╚═════════════════════════╝  │  │                     │  │  └───────────────────────┘    │
│             │                 │  │  • Session DEKs     │  │                               │
│       derives                 │  │    (encrypted)      │  │  ╔═════════════════════════╗  │
│             ▼                 │  │                     │  │  ║  machine_key (local)    ║  │
│  ╔═════════════════════════╗  │  │  • Machine DEKs     │  │  ╚═════════════════════════╝  │
│  ║  content_secret_key     ║  │  │    (encrypted)      │  │                               │
│  ╚═════════════════════════╝  │  │                     │  │  «═════════════════════════»  │
│             │                 │  │  • Session content  │  │  ║  session DEKs           ║  │
│       decrypts                │  │    (encrypted)      │  │  ║  (decrypted in memory)  ║  │
│             ▼                 │  │                     │  │  «═════════════════════════»  │
│  «═════════════════════════»  │  │  • Machine data     │  │                               │
│  ║  All DEKs (decrypted)   ║  │  │    (encrypted)      │  │  ❌ No master_secret          │
│  «═════════════════════════»  │  │                     │  │  ❌ No content_secret_key     │
│                               │  │  Cannot decrypt     │  │  ❌ Cannot decrypt other      │
│  ┌─────────────────────────┐  │  │  anything           │  │     sessions' DEKs           │
│  │  content_public_key     │  │  │                     │  │                               │
│  └─────────────────────────┘  │  │                     │  │                               │
│                               │  │                     │  │                               │
└───────────────────────────────┘  └─────────────────────┘  └───────────────────────────────┘

会话同步流程

CLI 创建会话时生成随机 DEK,用公钥加密后存到服务器;手机端用私钥解密 DEK 后才能读取内容。

══════════════════════════════════════════════════════════════════════════════
                           SESSION SYNC FLOW
══════════════════════════════════════════════════════════════════════════════

     MOBILE                            SERVER                         CLI
        │                                 │                             │
        │                                 │      New session created    │
        │                                 │              │              │
        │                                 │              ▼              │
        │                                 │  «═══════════════════════════════»
        │                                 │  ║ Generate random SESSION DEK   ║
        │                                 │  ║ (AES-256, 32 bytes)           ║
        │                                 │  «═══════════════════════════════»
        │                                 │              │              │
        │                                 │              ▼              │
        │                                 │  ┌─────────────────────────────────┐
        │                                 │  │ Encrypt DEK with               │
        │                                 │  │ content_public_key             │
        │                                 │  └─────────────────────────────────┘
        │                                 │              │              │
        │                                 │              ▼              │
        │                                 │  ┌─────────────────────────────────┐
        │                                 │  │ Encrypt session content with   │
        │                                 │  │ SESSION DEK (AES-256)          │
        │                                 │  └─────────────────────────────────┘
        │                                 │              │              │
        │                                 │◄┄┄┄┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┄┄┄┄│
        │                                 │  encrypted   │              │
        │                                 │  DEK +       │              │
        │                                 │  encrypted   │              │
        │                                 │  content     │              │
        │                                 │              │              │
        │◄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄│              │              │
        │                                 │              │              │
        ▼                                 │              │              │
  ╔════════════════════════════════╗      │              │              │
  ║ 1. Decrypt DEK with            ║      │              │              │
  ║    content_secret_key          ║      │              │              │
  ║                                ║      │              │              │
  ║ 2. Decrypt content with DEK    ║      │              │              │
  ╚════════════════════════════════╝      │              │              │
        │                                 │              │              │
        ▼                                 │              │              │
  ┌──────────────────┐                    │              │              │
  │ Display session  │                    │              │              │
  │ in mobile app    │                    │              │              │
  └──────────────────┘                    │              │              │
        │                                 │              │              │
        │   User sends command            │              │              │
        │              │                  │              │              │
        ▼              │                  │              │              │
  ┌─────────────────────────────────┐     │              │              │
  │ Encrypt with SESSION DEK        │     │              │              │
  └─────────────────────────────────┘     │              │              │
        │                                 │              │              │
        │┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄>│              │              │
        │                                 │┄┄┄┄┄┄┄┄┄┄┄┄┄>│              │
        │                                 │              ▼              │
        │                                 │  ┌─────────────────────────────────┐
        │                                 │  │ Decrypt with SESSION DEK       │
        │                                 │  │ (CLI has DEK in memory)        │
        │                                 │  └─────────────────────────────────┘
        │                                 │              │              │
        ▼                                 ▼              ▼              ▼

未来:与好友共享会话

共享时只需用好友的公钥重新加密 32 字节的 DEK,无需重新加密全部会话内容。

══════════════════════════════════════════════════════════════════════════════
                    FUTURE: SHARING WITH FRIENDS
══════════════════════════════════════════════════════════════════════════════

To share a session with a friend:

  YOUR MOBILE                         SERVER                    FRIEND'S MOBILE
       │                                 │                             │
       │                                 │                             │
  ╔════════════════════════════╗         │                             │
  ║ 1. Get friend's            ║         │                             │
  ║    content_public_key      ║◄────────│─────────────────────────────│
  ║                            ║         │  (friend's public key       │
  ║ 2. Decrypt session DEK     ║         │   is on their profile)      │
  ║    with YOUR secret key    ║         │                             │
  ║                            ║         │                             │
  ║ 3. Re-encrypt session DEK  ║         │                             │
  ║    with FRIEND's pub key   ║         │                             │
  ╚════════════════════════════╝         │                             │
       │                                 │                             │
       │┄┄┄┄ share record ┄┄┄┄┄┄┄┄┄┄┄┄┄┄>│                             │
       │     (DEK encrypted for friend)  │                             │
       │                                 │                             │
       │                                 │┄┄┄┄ share notification ┄┄┄┄>│
       │                                 │                             │
       │                                 │    ╔════════════════════════════════╗
       │                                 │    ║ Friend decrypts DEK with      ║
       │                                 │    ║ THEIR content_secret_key      ║
       │                                 │    ║                               ║
       │                                 │    ║ Friend can now decrypt        ║
       │                                 │    ║ session content with DEK      ║
       │                                 │    ╚════════════════════════════════╝
       │                                 │                             │
       ▼                                 ▼                             ▼

  KEY INSIGHT: Session content never re-encrypted!
  Only the small DEK (32 bytes) is re-encrypted for friend.
  Original encrypted content stays exactly the same on server.

密钥用途总结

密钥 生命周期 用途
MASTER SECRET ONCE/ACCOUNT 根密钥,派生一切。备份码就是它的 base32 编码
CONTENT KEY PAIR (公钥 + 私钥) ONCE/ACCOUNT 加密/解密 DEK;支持共享时无需重新加密内容
SESSION DEK (AES-256) ONCE/SESSION 加密会话消息和元数据;可重新加密给好友
MACHINE DEK (AES-256) ONCE/MACHINE 加密机器级数据;可按机器撤销
MACHINE KEY (local) ONCE/MACHINE 本地 CLI 缓存加密;从不发送到任何地方
EPHEMERAL KEYPAIR ONCE/AUTH QR 认证时的安全密钥交换;用完即弃

安全属性

不同泄露场景下的影响分析——即使 CLI 机器被入侵,攻击者也无法获取主密钥或其他会话。

泄露场景 攻击者能获取 攻击者无法获取
CLI 机器被入侵 content_public_key、machine_key (local)、当前会话的 session DEK(内存中)、本机器的会话内容 master_secret、content_secret_key、其他会话的 DEK、好友的会话
服务器被入侵 加密的 DEK、加密的内容、元数据 解密后的 DEK、任何明文内容、密钥
网络窃听 加密流量 任何有用信息
恢复场景 解决方案
CLI 机器丢失/被盗 从账户中移除——session DEK 只在内存中,machine DEK 被撤销
手机丢失 + 有备份码 恢复 master_secret → 重新派生 content keys → 可再次解密所有 DEK
手机丢失 + 无备份码 账户不可恢复(设计如此)

为什么这样设计

你的主密钥永远不会离开手机。 每台 CLI 机器只获得一个派生的公钥用于加密。这意味着:

  • CLI 机器被入侵时,攻击者无法获取你的备份码或影响其他机器
  • 服务器只存储它无法读取的加密数据块
  • 与好友共享会话时效率很高——只需重新加密 32 字节的密钥,而非全部内容

自建服务器

不信任我们的中继服务器?自己搭一个。详见 自建指南。