Repository navigation
openchat Android v0.6.0 预览版
Pre-release
Pre-release
预览版。第一个用专用发布密钥签名的版本,不能覆盖安装 v0.5.0 及更早的预览版(那些用的是调试密钥):请先卸载再安装。 卸载会丢失身份、收藏和验证记录,这是一次性的切换代价。证书指纹见 SECURITY.md。
和 v0.5.0 一样,尚未在真机上运行过,也没和 bitchat 手机实测互通。 这一版改动很多,请先装一台确认能正常打开。
新增(均与 bitchat 兼容)
- 私密群组:「私聊」页新建群组或
/group create,创建者邀请附近的人、移除成员,最多 16 人。名单和群密钥由创建者签名并在加密会话里分发,群消息用群密钥加密、作者签名,像公共消息一样在网状网里传播,只有成员能读。离开过的成员靠历史同步补消息。目前只支持文字。 - 布告板:「附近」页的公告,可钉 1、3 或 7 天,可标紧急,可撤下。帖子由作者签名,手机之间传递,包括历史同步,所以几天后到的人也能看到;每人 5 条,总共 200 条。
- 网状桥接(默认关闭,「我」页开关):开启后「附近」消息也会经互联网中继发到你所在约 1.2 km 的网格,其它"孤岛"里的人说的话会出现在「附近」,标为"经桥接"。有网的桥接手机替整个孤岛收发。
- 信使投递:桥接开启时,发给不在范围内的人的私信也会以一次性密钥封好、停放在中继上,任一方上线即送达。
- 地区频道网关:桥接开启且有网时,你的手机也替身边没网的人转发地区频道消息,双向都有速率限制,每条事件都验签。
/ping @名字和/trace @名字:看往返时间、跳数和估算路径。回应按链路预算,伪造的发送者不能把别人的手机变成"回应炮"。- 担保:你当面验证过的人会告诉你的手机他们验证过谁,这些人在卡片上显示灰色 ✓ 和"由 … 验证"。30 天过期;取消对担保人的验证,其担保随之失效。
- 一次性预共享密钥:每台手机发布 8 把单次密钥。发给不在范围内的人的信件在有可用密钥时用它封装,信件打开两天后密钥删除,之后手机被盗也打不开途中被截获的信件。
/pay与 Cashu 卡片:/pay <token>发送 Cashu 电子现金,先检查是否为真实代币;私聊之外要加public。消息里的代币显示为卡片,带金额、铸币方、备注和"在钱包中打开"。应用本身不和铸币方通信。- 实况语音:按住麦克风说话,松开发送(快速点一下仍是开始/停止录音)。对方在你还在说的时候就能听到。「我」页有开关,默认开。
- 地区频道分享:生成带
bitchat://geohash/链接的文字邀请,bitchat 和 openchat 都能打开。
隐私与防刷
- 洪泛保护与 bitchat 一致:每人一次 5 条、之后每秒 1 条;同一文本一次 3 条、之后每两秒 1 条。超出的消息隐藏但仍转发。
- 地区频道消息带 8 位工作量证明(NIP-13)。
- 通知默认不显示内容:"新消息",没有名字、地点和正文;「我」页可开预览,锁屏始终安静。
- 收到的照片先显示为粗块,点开才看清。
- 三连击清空时会停止 Tor、删除其目录并换新的入口节点重启,新身份与旧身份不共享电路;缓存里的媒体文件每次服务启动都清掉。
- 密钥用完即清零:放弃的握手、建立会话后的握手状态、三连击后的旧身份私钥和 Nostr 种子、身份文件的明文缓冲。
界面
- 应用 logo 移到主界面左上角,是 48 dp 的三连击目标;三次点击按间隔判断(每次 500 ms),每次有触感反馈。首次启动的安全提示在 18 种语言里都指向 logo。
- 统一图标风格:emoji 和 ■/▶ 换成 Material 图标,✓ ✈ ★ 为单色文本;底栏图标选中时变为实心。
- 默认昵称为
anon,名字里紧挨后缀的数字去掉,bitchat 的anon1234显示为anon#xxxx。 - 读屏支持:底栏图标不再重复读标签;录音键是真正的按钮(一次激活开始、再次激活发送,手势不变);播放键会念"播放/停止";发送者名字是 32 dp 按钮;验证二维码有描述。「我」页新增"清空所有数据"按钮,带确认,给用不了三连击的人。
- 聊天列表只在你位于末尾或消息是你自己的时候才跟随新消息,历史同步插入旧消息不再把你拽到底部;照片和语音时长在后台解码并缓存。
- 显式请求边到边绘制(Android 15 强制),首启页的语言菜单和开始按钮避开系统栏。
修复(2026-10-29 代码评审)
- Nostr 私信身份绑定:经 Nostr 到达的收藏通知曾可以把任意网状节点的身份绑到发送者自己的 npub 上,之后发给该人的远程私信会加密给攻击者。现在只有已记录的 npub 能代表一个人。
- 身份、网状状态、屏蔽名单文件先写旁路文件再改名覆盖,写一半崩溃不会留下半个文件;打不开的身份文件不再被新密钥替换,而是停止服务并提示重试。
- Noise 握手 10 秒超时并重试;垃圾密文或伪造的握手消息不再切断已建立的会话;半开握手上限 64 个;离开一小时的会话丢弃并清零密钥。
- 自己的公共消息和广播发给每个邻居,不再受 log₂(n) 扇出子集限制。
- 签名密钥与记录不符的广播即使在对方离线后也拒绝;收藏、已验证和有待发信件的人不会被陌生人洪泛挤出密钥表。
- Android 14+ 在蓝牙权限被撤销后重启服务不再崩溃循环。
- 发照片和语音不再阻塞 UI 线程。
- Nostr 送达回执每条消息只发一次,重连不再回放两天的回执。
- 蓝牙链路层:发送队列满时不再丢弃最旧的分片(那会把一帧切成两半),一帧要么整发要么整拒;分片按链路实际吞吐节奏发送;接收端的帧解析器会丢弃停滞 250 ms 的半帧、按类型限制载荷大小、逐字节重新同步;连续 40 次写失败的链路断开。
- 扫描占空比按规范 §11:扫 5 秒歇 10 秒(6 条以上链路时 3 秒 / 15 秒),central 表满了不扫,扫描启动失败按 10、20、40、60 秒退避。发现新邻居最多慢 10 秒,无线电三分之二的时间在休息。
- 连接超时后清理所有相关表项;同一链路上换了身份的设备,旧身份正常过期;重组后的包沿用分片的 TTL。
- 历史同步回复里的广播不再创建或刷新在线状态;同步副本不再取消正在等待的转发;新链路建立后立刻发送广播而不是等 15–38 秒。
- Tor 的开关状态取自服务状态而不是端口号;路由切换瞬间打开的 socket 直接丢弃;Tor 关闭时
.onion中继不会交给系统解析。
构建
- Gradle 8.14.6、AGP 8.13.2、Kotlin 2.4.21、Compose 1.11、compileSdk 36(targetSdk 仍为 35)。
Screens.kt拆为 7 个文件,纯搬动。
验证
161 个单元测试通过(比 v0.5.0 多 67 个),包括 bitchat 官方的消息 ID 测试向量。桥接的中继端逻辑尚无单元测试。
权限
无新增。
校验
shasum -a 256 openchat-0.6.0.apk 应为:
659b40d79cfadad5fd26c58ba1db5ce752425fcc76f9bd6058f9f9ad059886ca
证书指纹(apksigner verify --print-certs)应与 SECURITY.md 一致。