分享后的消息、历史、文件和终端复用现有单聊接口与 Socket 协议。消息仍走原来的单聊流程,不新增访客消息类型或另一套聊天接口;Studio 在入口增加临时 token 鉴权。App 在单聊 Header 提供分享抽屉;接收方通过官网链接领取,支持直连和云端受限转发。
- SQLite
session_shares每次分享新增一条记录,绑定唯一 session;群聊和 workflow session 不可使用此功能。 - Token 为
sst1_加 32 字节安全随机数的 Base64URL。数据库只保存 SHA-256,创建时返回一次明文;其他响应不返回 token 或哈希。App 按登录账号隔离存储,分享链接使用 fragment,不能使用 query 携带凭据。 - 从创建起固定 30 天过期;领取和修改权限不续期。
- 接收人登录 App 并明确确认后,SQLite 条件 UPDATE 原子绑定第一位接收人的 App 用户 ID、名称快照和领取时间。相同接收人重试幂等成功,其他人返回 409。不允许改绑;再次分享创建新记录。
- 分享者及接收人均使用云端 App 用户 ID;另存
created_by_user_id作为 Studio 本地执行账号,不能混用。名称快照由服务端验证后记录,不参与授权。 - 撤销保留记录。session 删除、移至其他 Profile、创建人被禁用或失去 Profile 授权时,访问失效。
HTTP 使用两个请求头:
X-Session-Share-Token: sst1_...
X-App-Access-Token: <接收人的短时分享身份凭证 ssp1_...>现有 /chat-run 和 /terminal Socket.IO 连接使用:
{
"auth": {
"shareToken": "sst1_...",
"appAccessToken": "<接收人的短时分享身份凭证 ssp1_...>"
}
}直连也支持 HTTP Authorization: Bearer sst1_... 或 Socket auth.token=sst1_...,仍需 App 云端凭据。经过已有 App Relay 时优先使用独立分享字段,防止 relay 注入本地 JWT 时覆盖 token。分享凭据优先:同时携带普通 JWT 不会扩大分享权限。URL query 不接受分享 token。
接收者先用自己的 App 登录向账号服务 POST /api/app/auth/session-share-proof 申请 ssp1_ 身份凭证。凭证固定绑定分享 token 的 SHA-256,最长 5 分钟,具有独立 audience/type,不能调用账号、支付、设备管理或普通 Relay。App 不向分享者的服务器发送完整云端登录 token;云端转发也签发同样的限定凭证。
Studio 向自身配置的 App 线路 /api/app/auth/session-share-identity 核验此凭证、分享哈希、账号和登录设备状态、ID、名称及 Studio 使用权益。不接受客户端提交的身份或任意验证 URL。分享管理仍使用 /api/app/auth/me 验证分享者身份和权益。身份缓存最多 10 秒且不超过 access token 到期时间;云端故障时失败关闭。这里的“仅 App”由登录身份和产品入口定义,不以 User-Agent 作为鉴权依据。
管理接口另外需要 Authorization: Bearer <Studio app_access JWT>。普通网页账号 JWT 和分享 token 都不能创建或管理分享。
| 方法与路径 | 用途 |
|---|---|
POST /api/studio/sessions/:sessionId/shares |
创建;{ permissions?, extraPaths? };201 返回 { share, token } |
GET /api/studio/sessions/:sessionId/shares |
当前分享者的记录,包括过期和撤销记录 |
PATCH /api/studio/sessions/:sessionId/shares/:shareId |
部分修改 permissions;extraPaths 提供时替换白名单 |
DELETE /api/studio/sessions/:sessionId/shares/:shareId |
幂等撤销,保留记录 |
POST /api/studio/session-shares/claim |
{ "confirm": true } 主动领取 |
GET /api/studio/session-shares/access |
当前 session 绑定、权限和到期元数据 |
POST /api/studio/session-shares/check |
{ action, sessionId } 权限预检 |
管理操作同时核验本地 Profile 权限和原分享者的 App、本地账号。预检结果不能代替业务接口鉴权。未知字段和自定义过期时间、用户 ID 均被拒绝。HTTP 分享响应设置 Cache-Control: no-store、Referrer-Policy: no-referrer。
领取后的基本 read 权限可以读取绑定 session 的历史、上下文、用量并恢复消息流。可配置权限默认 false:
| 字段 | 现有接口或操作 |
|---|---|
input |
POST /api/studio/chat-run/runs;Socket run、abort、排队操作、批准和澄清回复 |
voice |
会话限定的语音识别、合成;App 语音输入和对话还需 input |
upload |
/api/studio/uploads 和 /api/studio/app-uploads 的创建、分片、完成、取消 |
download |
/api/studio/files/download、session 导出、workspace-file/content?download=1 |
workspaceRead |
session 的 workspace-files/list、文件读取、预览、diff 和运行文件变更 |
workspaceWrite |
session 的文件写入、mkdir、删除、rename、copy |
outsideWorkspace |
在额外目录白名单内应用文件权限;不单独授予整个文件系统访问能力 |
terminal |
现有 /terminal Socket 协议,source=single、sourceId=绑定的 session |
压缩导出会调用模型,因此除 download 外还需 input。
只允许明确列出的 session 路由;全局列表、搜索、设置、其他会话、分享管理和会话删除等接口被拒绝。Socket 只允许 resume、app.resume 和上述输入事件,不订阅全局 App 事件或 Profile 广播。基本读取不返回父会话的标题和消息预览。
发送消息必须提供绑定的 session_id,不能创建新会话。执行配置从现有 session 获取;客户端不能通过此请求覆盖 Profile、工作区、模型凭据、MCP、群聊或 workflow 上下文。管理会话的斜杠命令不开放给分享 token。
终端仍走原协议并使用现有宿主机权限,创建人也须具备现有终端权限。终端归属包含 share ID,接收人不能附着主人或另一条分享记录的终端;同一分享重连可恢复自己的 PTY。
额外目录格式为 extraPaths: [{ path: "/absolute/directory", writable: false }],最多 16 项,必须存在且由本地超级管理员授权。outsideWorkspace=true 要求非空白名单。工作区和额外目录固定真实路径,文件入口检查路径穿越、符号链接越界及已知凭据路径(.env*、.token、.model-run-token、auth.json、.ssh 等)。rename/copy 同时校验两端。工作区改变后需重新分享。
分享上传写入由服务端按 session 划分的附件目录;上传分片还绑定具体 share ID。upload 不等于工作区写权限。下载只允许授权工作区/额外目录或该 session 的分享上传目录,不能按任意路径读取整个 Profile 的附件。会话附件会自动纳入文件访问范围,无需开启 outsideWorkspace:分享者发送的结构化上传附件按会话持久化登记;首次分享之前的历史结构化附件按文件匹配补录。普通聊天文本、Markdown 链接和其他会话附件不会产生授权。旧版本在首次分享之后写入且缺少可信归属的附件不自动补录。附件预览仍需 workspaceRead,下载仍需 download;自动纳入不授予目录浏览、工作区写入或删除权限。
按本功能约定,权限只限制 App 对接口的直接调用。Agent 和终端沿用既有执行权限,不引入文件系统或工具沙箱。 开启 input 后,Agent 能执行原有工具;开启 terminal 后,shell 能执行宿主机账号允许的命令。因此 outsideWorkspace=false 不限制 Agent/shell 的间接文件访问,关闭 download 也不是对已显示内容的防复制措施。
每次 HTTP 请求和 Socket 命令都检查接收人、到期、撤销、绑定 session 和动作权限。分享策略按 token 哈希缓存,最多 1024 项、10 秒 TTL;session/创建人的授权状态仍读取当前数据。修改和撤销提交后同步清理缓存并通知观察者,使用 policy_version 条件更新避免并发覆盖。
待执行消息在出队时重新校验 App 身份和输入权限,不因换了处理队列的连接而继承其他账号权限。已接受并开始执行的 Agent 任务沿用现有运行生命周期,撤销阻止新的接口操作和后续排队消息,不回滚已发生的执行副作用。
Socket 和 PTY 通过 watch 监听策略变化;本进程权限修改会关闭连接,过期和创建人失权的定时复查间隔为 1 秒。App 身份也持续复查,受最多 10 秒身份缓存约束。已断开连接但仍存活的 PTY 同样在撤销后终止。正常断开只解除 writer,保留仍有效的终端以支持重连。
多个 Studio 进程共用数据库时,其他进程的策略缓存最多滞后 10 秒;跨进程即时撤销需要额外广播。路径检查并非操作系统沙箱,无法替代对有宿主机执行权限的并发进程的隔离。
云端 session-share Relay 无需绑定分享者设备,只允许指定 session 的接口和 /chat-run、/terminal,并固定身份凭据及终端上下文。云端转发继续要求现有云端权益;直连由 Studio 检查 Studio 使用权益。
App 保存分享连接和领取记录时按账号隔离,使用 https://ekkostudio.xyz/share/session/#HSC1.… 官网链接和 hermes-studio://share/session?sessionInvite=… 唤起。官网不解析会话或领取 token。App 在凭证到期前更新身份并重新连接对应 Socket,重新恢复单聊状态。
部署依赖:先发布云端身份凭证和 Relay 接口、官网落地页,再发布支持凭证验证的 Studio 与 App。旧云端没有权益字段时失败关闭,不降级跳过购买校验。
相关测试包含 session-shares、session-share-app-identity、session-shares-routes、session-share-access、普通账号鉴权、排队执行、会话文件控制器、App 上传、Relay 和移动终端 Socket 测试。集成测试使用隔离 SQLite、临时目录和模拟云端身份/PTY,不创建真实分享或运行真实 Agent。
voice(允许使用语音)默认关闭,旧分享记录也按关闭处理。开启后允许朗读回复;App 的语音输入和语音对话还需开启 input,识别后的文本发送继续执行原有 input 鉴权。
POST /api/studio/sessions/:id/share-voice/synthesize:仅接受{ text },最多 5,000 字符,输出 MP3。POST /api/studio/sessions/:id/share-voice/transcribe:仅接受 multipart 单个audio文件,无需upload权限。
Studio 使用分享绑定 Profile 已配置的语音服务,拒绝接收者指定 provider、凭据或 options,不开放全局语音设置。沿用语音请求体限制和中转媒体大小限制。关闭语音权限会中止服务端待完成请求,App 停止录音和播放,丢弃迟到结果。直连和云端中转均使用同一组会话限定接口。