OTS 介绍与选型指南

一次性秘密分享平台 OTS(Luzifer/ots)介绍与选型:零知识加密模型、核心架构、功能特性、适用场景与同类组件量化对比,基于 v1.23.1 官方信息(2026-10-30 实查)。

OTS自托管安全Golang隐私安全2026/10/30

OTS 介绍与选型指南

目标:全面介绍一次性秘密分享平台 OTS(Luzifer/ots)的定位、加密模型、架构、功能与适用场景,并给出与同类自托管秘密分享组件的量化选型对比,帮助读者判断「要不要用 OTS、什么场景用」。本文为认知篇,全部信息溯源自官方仓库(GitHub Luzifer/ots)与官方 Wiki,查询日期 2026-10-30,对应最新稳定版 v1.23.1(2026-10-23 发布)。

一、环境信息

本篇为介绍与选型认知篇,不依赖本地部署环境,信息来源如下(均于 2026-10-30 实查):

信息项来源内容
最新稳定版本GitHub Releases(releases.atom)v1.23.1,发布于 2026-10-23
社区活跃度shields.io 实时徽章807 stars / 99 forks
开源许可仓库 LICENSE / shields.io 徽章Apache-2.0
官方文档仓库 README、官方 Wiki Customization 页定制参数、CLI 用法
官方公共实例ots.fyi作者托管演示站
官方镜像发行件ghcr.io/luzifer/ots官方镜像发布通道

💡 说明:OTS 存在版本节奏快的特征——2026 年 9 月至 10 月间连续发布了 v1.22.0(2026-09-27)、v1.23.0(2026-10-06)、v1.23.1(2026-10-23)三个稳定版,主干依赖由 renovate 机器人自动跟进,选型时以 Releases 页最新稳定版为准。

二、软件概述

2.1 项目定位

OTS(One-Time-Secret,仓库 Luzifer/ots)是一个一次性秘密分享平台:把一段敏感文本(密码、API Key、恢复码等)生成一个「只可读取一次」的分享链接,链接被打开一次后内容立即销毁。官方 README 对其核心机制的定义(查询日期 2026-10-30):

The secret is encrypted with a symmetric 256bit AES encryption in the browser before being sent to the server. Afterwards an URL containing the ID of the secret and the password is generated. The password is never sent to the server so the server will never be able to decrypt the secrets it delivers with a reasonable effort. Also the secret is immediately deleted on the first read.

拆解为三个关键设计点:

  1. 浏览器端加密:秘密在发送前就在浏览器内用对称 256 位 AES 加密成密文,服务器从头到尾只接触密文;
  2. 零知识(服务端不可解密):解密密码不随请求发送到服务器,只保留在分享链接的 # 锚点部分——按浏览器行为规范,锚点不会随 HTTP 请求发出,因此服务器「永远无法以合理代价解密它所投递的秘密」;
  3. 阅后即焚:秘密在首次读取后立即删除,无论是否到期。

2.2 身份信息速览

项目内容
项目名OTS(One-Time-Secret),仓库 Luzifer/ots
作者Knut Ahlers(GitHub ID:Luzifer)
首个公开提交2017 年(仓库 ID 99253773,活跃维护至今)
当前稳定版v1.23.1(2026-10-23,Releases 实查)
服务端语言Go(go.mod 声明 go 1.26.0,查询日期 2026-10-30)
前端Vue 3 单页应用,编译期内嵌进 Go 二进制(main.go 中 //go:embed frontend/*)
许可Apache-2.0
社区规模807 stars / 99 forks(shields.io,2026-10-30 实查)
发行形态官方二进制(GitHub Releases,linux/darwin/windows × amd64/arm/arm64)+ 官方镜像(ghcr.io/luzifer/ots)+ 源码编译;另发行配套 CLI 客户端 ots-cli

2.3 与 OneTimeSecret 的名称辨析

💡 技术拓展:搜索「一次性秘密分享」会同时命中两个名字很像的项目,选型时务必区分——

名称仓库技术栈加密位置关系
OTS(本文主角)Luzifer/otsGo 单二进制浏览器端(服务端零知识)独立项目
OneTimeSecretonetimesecret/onetimesecretRuby + Redis服务端(Redis 中存可解密内容)另一独立项目,不是 OTS 的别名

两者解决的问题同源(一次性链接分享),但加密模型不同:OTS 的服务器永远无法解密秘密,OneTimeSecret 的服务器持有完整可解密数据(安全性取决于服务器本身)。本站两个组件均有专栏,部署与 API 细节可对照阅读:《OneTimeSecret v0.26.19 介绍与选型指南》。

三、核心架构

3.1 组件构成

OTS 是典型的「单二进制 + 内嵌前端 + 可插拔存储」架构(依据:main.go 源码与 README,查询日期 2026-10-30):

浏览器(Vue3 前端,编译期内嵌于二进制)
   │  ① 用户输入秘密 → 前端生成随机密码
   │  ② 浏览器内 AES-256 加密,得到密文
   │  ③ 仅密文 POST /api/create(密码不出浏览器)
   ▼
ots 服务进程(Go,单二进制)
   ├── 路由层:/api/create、/api/get/{id}、/api/settings、/api/healthz、/api/isWritable、/metrics
   ├── 存储抽象层:storage-type = mem(内存)或 redis
   └── 指标层:Prometheus /metrics(按网段 ACL 控制)

三个要点:

  • 前端即密钥生成器:随机密码由前端生成并拼进 https://<域名>/#<secret_id>|<password> 的锚点里;服务器日志里只会出现 /(加载前端页面)与 /api/get/<id>(取密文),见不到密码与明文。
  • 存储层只存密文与过期时间:内存后端是一个带过期清理的 map;Redis 后端用 SET key value EX <expireIn> 原子写入(源码 pkg/storage/redis/redis.go 的 Create 实现,查询日期 2026-10-30),键前缀默认 io.luzifer.ots。
  • 读取即销毁:读取走 ReadAndDestroy,单次 GET 语义完成「取出 + 删除」,不存在「已读标记延迟删除」窗口。

3.2 一次分享的完整生命周期

以「把数据库口令发给同事」为例,数据流如下(依据官方 README 与源码 api.go,查询日期 2026-10-30):

  1. 发送方打开前端,输入秘密(可选附件、可选有效期),前端生成随机密码并本地加密,POST /api/create 上送密文,得到 secret_id(UUID);
  2. 前端展示完整链接(# 后含 secret_id|password),发送方通过聊天工具等渠道发给接收方;
  3. 接收方打开链接,前端从锚点解析出 id 与密码,GET /api/get/<id> 拉回密文并在浏览器解密展示;服务端同时删除该条目;
  4. 到期未读的条目由存储层过期机制淘汰(mem 后端周期清理、Redis 后端 TTL 自动过期);
  5. 任何人再以同一 id 请求,得到 404 secret_not_found。

💡 技术拓展:URL 中 # 后的部分叫「锚点(fragment)」。HTTP 规范(RFC 3986/9110)规定浏览器发起请求时不会把 fragment 发送给服务器,因此「密码放锚点」是业内通行的零知识做法——同类实现还有 Password Pusher、PrivateBin 等。代价是:如果接收方复制链接时漏掉 # 之后的内容,将无法解密;且链接一旦整体泄露给第三方,第三方同样能完成一次读取(链接本身就是「钥匙 + 锁」的组合)。

四、功能特性

依据官方 README 与 Wiki(查询日期 2026-10-30),v1.23.1 的功能面:

功能说明
浏览器端 AES-256 加密密文上送,服务端零知识(README「Features」原文列第一项)
一次性读取首读即删,ReadAndDestroy 单事务语义
有效期控制服务端上限 --secret-expiry;创建时可按 expiryChoices 覆盖(如 5 分钟 ~ 7 天)
加密附件附件与文本一起加密成单个 blob(官方 Wiki:附件上限默认 64 MiB,前端实施)
双主题 / 多语言暗色/亮色切换;i18n.yaml 驱动的社区翻译(含中文),可切正式/非正式称谓
二维码分享前端内置 secret URL 二维码生成(可关闭)
品牌定制customize.yaml 覆盖标题、图标、页脚链接、可覆盖前端任意静态资源(overlayFSPath)
Prometheus 指标/metrics 暴露创建/读取/错误计数,按子网 ACL 控制
TLS 直出--enable-tls + 证书/私钥参数,可脱离反向代理直接 HTTPS
官方 API 与 CLIREST API(OpenAPI 规范随仓库发布)+ 配套命令行客户端 ots-cli(create/fetch)
双存储后端mem(零依赖)与 redis(可持久化、可多实例)

五、快速开始

以下路径均引自官方 README 与 Releases 页(查询日期 2026-10-30),供认知评估;逐条命令的完整实测与回显见本专栏部署篇。

5.1 最小启动(官方二进制)

官方推荐路径:从 Releases 下载对应平台压缩包,解压后直接运行,默认监听 :3000:

# 下载并解压官方 linux amd64 发行包(Releases 页获取 v1.23.1)
curl -LO https://github.com/Luzifer/ots/releases/download/v1.23.1/ots_linux_amd64.tgz
tar xzf ots_linux_amd64.tgz
./ots --listen=:3000
# 启动后浏览器访问 http://localhost:3000/

README 原文对应说明:「Start it and you can access the server on http://localhost:3000/」(官方 README「Setup」节,查询日期 2026-10-30)。

5.2 官方 CLI 一次创建/读取(README 示例)

官方 README 给出的 ots-cli 端到端示例(示例回显为 README 原文,查询日期 2026-10-30):

# echo "my password" | ots-cli create
INFO[0000] reading secret content...
INFO[0000] creating the secret...
INFO[0000] secret created, see URL below                 expires-at="2023-10-16 16:33:27.422174121 +0000 UTC"
https://ots.fyi/#37a75a7f-0c2d-4ae6-bcca-4208b6d596ab%7CHGShVWm5umv4lmswfM73

# ots-cli fetch 'https://ots.fyi/#37a75a7f-0c2d-4ae6-bcca-4208b6d596ab%7CHGShVWm5umv4lmswfM73'
INFO[0000] fetching secret...
my password

URL 中 %7C 即 |,分隔 secret_id 与浏览器侧解密密码。

5.3 手工加密上送(README 强烈推荐姿势)

README「strongly recommended」的脚本化流程:先用 openssl 在本地加密,再上送密文(示例为 README 原文,查询日期 2026-10-30):

# echo "my password" | openssl aes-256-cbc -base64 -pass pass:mypass -pbkdf2 -iter 300000 -md sha512
U2FsdGVkX18wJtHr6YpTe8QrvMUUdaLZ+JMBNi1OvOQ=

# curl -X POST -H 'content-type: application/json' -i -s -d '{"secret": "U2FsdGVkX18wJtHr6YpTe8QrvMUUdaLZ+JMBNi1OvOQ="}' https://ots.fyi/api/create
{"secret_id":"5e0065ee-5734-4548-9fd3-bb0bcd4c899d","success":true}

随后把 https://ots.fyi/#5e0065ee-5734-4548-9fd3-bb0bcd4c899d|mypass 发给接收方。该姿势连 ots-cli 都不依赖,任何能跑 openssl 的机器都能完成加密。

六、适用场景与限制

6.1 适用场景

  1. 运维/开发口令交接:给同事、外包、值班人临时发数据库 root 口令、服务器密码,避免口令永久沉淀在聊天记录里;
  2. CI/CD 凭据人工兜底传递:流水线凭据轮换时把新 Secret 递给值班工程师,读一次即焚;
  3. 客服/交付场景:把一次性激活码、初始管理密码交给客户,客户读走后链接立即失效;
  4. 内部工单补充材料:附带上敏感配置片段(附件同样加密),避免粘贴到工单系统。

6.2 限制与边界(依据官方 Wiki 与源码,查询日期 2026-10-30)

  • mem 后端重启即丢:官方明确「In memory storage (wiped on restart of the daemon)」——重启后所有未读秘密消失,生产建议 Redis 后端;
  • 无用户体系与审计:OTS 没有登录、没有按人权限、没有读取人身份记录,访问控制靠网络边界(反向代理、内网)与 /metrics 子网 ACL;
  • 大小上限:前端附件上限 64 MiB,服务端 maxSecretSize 默认约 115.6 MiB(65 MiB 附件经双重 base64 膨胀后的内部默认值,Wiki Customization 页);
  • 一次性语义不可回退:接收方误操作(如刷新页面、复制链接不完整)导致没读到内容时,秘密已销毁,只能重新生成;
  • 链接即凭据:任何拿到完整链接(含锚点)的人在有效期内都能完成这一次读取,分享通道本身的保密性依赖发送渠道;
  • 过期覆盖受服务端约束:--secret-expiry 是硬上限,前端可选的有效期只能是 expiryChoices 的子集,且不能超过服务端上限。

七、同类选型对比

一次性秘密分享属于「安全类自托管小工具」赛道,候选较多。下表为可自托管的主流同类组件量化对比(各项目数据查询日期 2026-10-30,来源为各官方仓库/官网;本站已有专栏的组件给出可点击链接):

组件技术栈加密位置存储后端许可社区热度(2026-10-30 实查)部署复杂度适合谁
OTS(本文)Go 单二进制 + Vue3 内嵌浏览器端(零知识)mem / redisApache-2.0807 stars / 99 forks低(单二进制 + systemd)想要零知识 + 极简部署的团队
OneTimeSecretRuby + Sinatra + Redis服务端(Redis 存可解密内容)redis其他(Apache-2.0 分叉版)onetimesecret 官方托管站规模大中(Ruby 运行时 + Redis)需要邮件投递、品牌化分享页的团队
Password Pusher(pwpush)Ruby on Rails可选(URL 锚点/盲模式)数据库/redis/文件Apache-2.0pwpush 官方仓库约 2.6k stars中(Rails 栈全家桶)需要审计日志、多语言、URL/文件/多模式推送
PrivateBinPHP浏览器端(零知识)文件系统/数据库zlib/libpng约 6.8k stars低(PHP + Web 服务器)偏「加密粘贴板」,讨论安全类内容而非专用口令
YopassGo浏览器端(零知识)memcached / redisApache-2.0约 2.3k stars低~中与 OTS 定位最接近的 Go 系备选
HemmeligNode.js服务端加密sqlite / mysql / postgresMIT约 1k stars低~中想要 Web 管理台 + 组织内团队功能的场景

关键差异解读:「加密位置」是这条赛道的分水岭——OTS/PrivateBin/Yopass 属于浏览器端加密(服务器被攻破也拿不到明文),OneTimeSecret/Password Pusher 默认模式下服务器持有可解密数据(换来的是邮件投递、审计等平台能力);部署复杂度上 OTS 是唯一「一个二进制跑起来就行」的(前端内嵌、mem 后端零依赖),代价是功能面比 Password Pusher 这类平台型项目窄。再往上的长期凭据管理(密码库、团队共享保险库)不是这条赛道的任务,应选密码管理器类组件,如 《Vaultwarden 介绍与选型指南》(Bitwarden 兼容服务端)、《AliasVault 0.31.1 介绍与选型指南》(别名+密码双身份管理),两者解决的是「凭据长期存放与检索」,与 OTS 的「一次性投递」互补而非竞争。

💡 技术拓展:同为「一次性」语义,还有一类「一次性券码/兑换码」组件(发码、核销、防重复领取),如 《VoucherVault 1.30.3 介绍与选型指南》,它面向的是「码的有效性与核销流程」而非「内容的加密投递」。如果需求是发优惠券/兑换码选 VoucherVault 一类;如果是发口令/密钥并要求读完即焚,选 OTS 一类。

「如果…则…」选型建议:

  • 如果只想最快跑起来且介意服务器零知识 → OTS(单二进制 + mem 十秒可用,后续切 Redis);
  • 如果需要邮件通知接收人、分享页品牌化、读取审计 → OneTimeSecret 或 Password Pusher;
  • 如果团队已有 PHP 栈且主要诉求是加密粘贴板 → PrivateBin;
  • 如果要长期管理密码/密钥(而非一次性投递)→ Vaultwarden / AliasVault;
  • 如果发的是券码类一次性内容 → VoucherVault。

八、参考链接