mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-10-26 16:55:08 +00:00
feat(liveshare-protocol): reserve actor credentials and direct connections
Additive wire evolution: an actor may carry an owner-verifiable credential (type-checked here, verified by the owner, absent keeps today's Relay assertion), and a share spec may carry a connection that defaults to relay while a Relay that owns the data path refuses direct. Capabilities actor-credential and direct-connection declare support; the recorded frames carry both fields.
This commit is contained in:
1 parent
a2c5d40e30
commit
4800bbed30
6 files changed
+57
-10
No files matched your search
@@ -30,8 +30,8 @@
|
||||
en: d80868b3f0050542
|
||||
zh: 56bad4bab440b2bd
|
||||
/deepseek-ai-dsh-experimental-liveshare-protocol/known-limitations-and-deferred-work:
|
||||
en: 9a1023be7ada2b5f
|
||||
zh: 39845d3097636f94
|
||||
en: 9061ed82c5c3f8ea
|
||||
zh: 2c7a6f5c9dae117f
|
||||
/deepseek-ai-dsh-experimental-liveshare-protocol/known-limitations-and-deferred-work/dev-note:
|
||||
en: e67f633dc6fe7773
|
||||
zh: e88aeb617ffb74ea
|
||||
@@ -75,7 +75,7 @@ Relay compatibility uses declared versions, capabilities, and explicit refusals,
|
||||
<a id="known-limitations-and-deferred-work"></a>
|
||||
|
||||
- Gateway requests require the Relay's authenticated actor outside the opaque body; frames without that actor are refused.
|
||||
- The Owner Host and worker ship together. The supervisor mints a branded UUID `registrationToken` per logical add, invalidates it on removal, and replays it only for the same retained registration after process restart. Lifecycle, requests, and cancellations echo that token with the share id; per-request `TunnelRequestToken` remains independent. IPC decoders refuse missing identities, and neither token enters Relay URLs or wire frames. Same-package IPC does not negotiate versions; Relay wire frames retain numeric ids and protocol version 1. Compatibility capabilities include `file-upload`: a relay registration that declares it may carry `allowsFileUpload` on the share support and access-state frames; every other peer keeps the stored value and a frame without the field reads as uploads off.
|
||||
- The Owner Host and worker ship together. The supervisor mints a branded UUID `registrationToken` per logical add, invalidates it on removal, and replays it only for the same retained registration after process restart. Lifecycle, requests, and cancellations echo that token with the share id; per-request `TunnelRequestToken` remains independent. IPC decoders refuse missing identities, and neither token enters Relay URLs or wire frames. Same-package IPC does not negotiate versions; Relay wire frames retain numeric ids and protocol version 1. Compatibility capabilities include `file-upload`, `actor-credential`, and `direct-connection`. An actor may carry an optional owner-verifiable `credential` (the protocol validates its type only; an owner verifies it before trusting the asserted identity, and absence keeps today's Relay-asserted semantics). A share spec may carry `connection`; absence reads as `{ mode: 'relay' }`, and a Relay that still owns the data path refuses a `direct` registration.: a relay registration that declares it may carry `allowsFileUpload` on the share support and access-state frames; every other peer keeps the stored value and a frame without the field reads as uploads off.
|
||||
- Owners declaring `share-access-state` receive committed, id-less ACL/mode/expiry snapshots with a durable monotonic version. The worker forwards snapshots only from its current socket; the Host clears authority on socket loss and refreshes the authenticated requests snapshot before external execution admission. An unavailable snapshot fails closed instead of borrowing persisted ACLs.
|
||||
- **Historical release support is not verified** — `tests/frames/v1` records concrete frames and refusal obligations, but does not identify a published Owner release. The gateway declares `LIVESHARE_PROTOCOL_VERSION` and implemented capabilities; the Relay rejects a declared mismatch and sends optional lifecycle frames only to declared consumers. These checks preserve recorded fields and explicit refusals, not a per-release translation or an unbounded support window.
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ Relay 兼容性依靠声明版本、能力和明确拒绝,不提供逐发布
|
||||
<a id="known-limitations-and-deferred-work"></a>
|
||||
|
||||
- 网关请求必须在不透明请求体之外携带 Relay 已认证主体;没有该主体的帧被拒绝。
|
||||
- Owner Host 与 worker 同包发布。Supervisor 为每次逻辑添加生成品牌化 UUID `registrationToken`,移除时使其失效,进程重启只为仍保留的同一注册重播该身份。生命周期、请求及取消事件回传该 token 与分享 ID;逐请求 `TunnelRequestToken` 独立存在。IPC 编解码拒绝缺失身份,两种 token 都不进入 Relay URL 或线协议帧。同包 IPC 不协商版本;Relay 线协议帧保留数字 ID 与协议版本 1。兼容能力包含 `file-upload`:声明它的 Relay 注册可在分享支持与 access-state 帧上携带 `allowsFileUpload`;其它 peer 保留已存值,而缺少该字段的帧读作上传关闭。
|
||||
- Owner Host 与 worker 同包发布。Supervisor 为每次逻辑添加生成品牌化 UUID `registrationToken`,移除时使其失效,进程重启只为仍保留的同一注册重播该身份。生命周期、请求及取消事件回传该 token 与分享 ID;逐请求 `TunnelRequestToken` 独立存在。IPC 编解码拒绝缺失身份,两种 token 都不进入 Relay URL 或线协议帧。同包 IPC 不协商版本;Relay 线协议帧保留数字 ID 与协议版本 1。兼容能力包含 `file-upload`、`actor-credential` 与 `direct-connection`。actor 可携带可选的、owner 可验证的 `credential`(协议只校验类型;owner 在采信断言身份前自行验证,缺失时保持今天由 Relay 断言的语义)。分享 spec 可携带 `connection`;缺失读作 `{ mode: 'relay' }`,仍拥有数据面的 Relay 会拒绝 `direct` 注册。:声明它的 Relay 注册可在分享支持与 access-state 帧上携带 `allowsFileUpload`;其它 peer 保留已存值,而缺少该字段的帧读作上传关闭。
|
||||
- 声明 `share-access-state` 的 owner 接收已提交、无 id 的 ACL、模式与到期时间快照,快照携带持久单调版本。worker 只转发当前 socket 的快照;Host 在 socket 丢失后清空授权,并在外部执行准入前刷新已认证的请求快照。快照不可用时默认拒绝,不借用持久化 ACL。
|
||||
- **历史发布版本支持尚未核实**——`tests/frames/v1` 记录具体帧和拒绝义务,但没有标识已发布的 Owner 版本。网关声明 `LIVESHARE_PROTOCOL_VERSION` 与已实现能力;Relay 拒绝已声明的版本不匹配,仅向已声明消费者发送可选生命周期帧。这些检查保留已录字段和明确拒绝,不提供逐发布版本翻译或无限支持窗口。
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ export const LIVESHARE_CAPABILITIES: readonly string[] = [
|
||||
'unsupported-frame-refusal',
|
||||
'share-access-state',
|
||||
'file-upload',
|
||||
'actor-credential',
|
||||
'direct-connection',
|
||||
]
|
||||
|
||||
/**
|
||||
@@ -51,6 +53,8 @@ export interface SharePermissions {
|
||||
|
||||
/** One share the owner host asks its gateway worker to carry to the Relay. */
|
||||
export interface TunnelShareSpec extends SharePermissions {
|
||||
/** How a viewer reaches the owner; absent reads as `{ mode: 'relay' }`, today's only shape. */
|
||||
readonly connection?: ShareConnection
|
||||
readonly shareId: string
|
||||
readonly sessionId: string
|
||||
/** Display-only share title; the host refreshes it with `reconcileShareTitles()` when renamed. */
|
||||
@@ -166,6 +170,23 @@ export interface ShareRpcActor {
|
||||
readonly providerId: string
|
||||
readonly tenantId: string
|
||||
readonly subjectId: string
|
||||
/**
|
||||
* Owner-verifiable credential asserting this identity, when the deployment's data path has no
|
||||
* in-band authority to assert it (a viewer reaching the owner directly). Absent today: the Relay
|
||||
* asserts the triple itself. The owner verifies it before trusting the identity; the protocol does
|
||||
* not verify it.
|
||||
*/
|
||||
readonly credential?: string
|
||||
}
|
||||
|
||||
/** How a viewer reaches the owner that published this share. */
|
||||
export interface ShareConnection {
|
||||
/**
|
||||
* `relay` reaches the owner through the Relay that carried this registration. `direct` is reserved
|
||||
* for a deployment where viewers reach the owner itself; its transport parameters are added when
|
||||
* that path is implemented.
|
||||
*/
|
||||
readonly mode: 'relay' | 'direct'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -484,6 +505,8 @@ function isTunnelShareSpec(value: unknown): value is TunnelShareSpec {
|
||||
&& (value.allowsFork === undefined || typeof value.allowsFork === 'boolean')
|
||||
&& (mode === undefined || mode === 'view' || mode === 'write')
|
||||
&& (value.allowsFileUpload === undefined || typeof value.allowsFileUpload === 'boolean')
|
||||
&& (value.connection === undefined
|
||||
|| (isRecord(value.connection) && (value.connection.mode === 'relay' || value.connection.mode === 'direct')))
|
||||
&& (value.ownerCredential === undefined || typeof value.ownerCredential === 'string')
|
||||
&& Array.isArray(value.allowedSubjectIds)
|
||||
&& value.allowedSubjectIds.every(subject => typeof subject === 'string' && subject !== '')
|
||||
@@ -501,6 +524,7 @@ function isShareRpcActor(value: unknown): value is ShareRpcActor {
|
||||
return typeof actor.providerId === 'string' && actor.providerId !== ''
|
||||
&& typeof actor.tenantId === 'string'
|
||||
&& typeof actor.subjectId === 'string' && actor.subjectId !== ''
|
||||
&& (actor.credential === undefined || typeof actor.credential === 'string')
|
||||
}
|
||||
|
||||
function isRegistrationToken(value: unknown): value is TunnelRegistrationToken {
|
||||
|
||||
+13
-3
@@ -11,15 +11,25 @@
|
||||
"relay": "wss://relay.example",
|
||||
"gatewayToken": "token-one",
|
||||
"ownerDisplayName": "Owner",
|
||||
"allowedSubjectIds": ["ou_viewer", "example:tenant-a:user-other"],
|
||||
"allowedSubjectIds": [
|
||||
"ou_viewer",
|
||||
"example:tenant-a:user-other"
|
||||
],
|
||||
"sessionMode": "write",
|
||||
"allowsFork": true,
|
||||
"allowsFileUpload": true,
|
||||
"dshVersion": "0.2.0-rc.2",
|
||||
"ownerInstanceId": "instance-one",
|
||||
"expiresAt": "2026-10-03T00:00:00.000Z"
|
||||
"expiresAt": "2026-10-03T00:00:00.000Z",
|
||||
"connection": {
|
||||
"mode": "relay"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["type", "share", "share.allowsFileUpload"],
|
||||
"required": [
|
||||
"type",
|
||||
"share",
|
||||
"share.allowsFileUpload"
|
||||
],
|
||||
"optional": []
|
||||
}
|
||||
+16
-3
@@ -4,9 +4,22 @@
|
||||
"frame": {
|
||||
"type": "share-rpc",
|
||||
"id": 7,
|
||||
"body": { "op": "call", "path": "/__share/context", "body": {} },
|
||||
"actor": { "providerId": "example", "tenantId": "tenant-a", "subjectId": "ou_viewer" }
|
||||
"body": {
|
||||
"op": "call",
|
||||
"path": "/__share/context",
|
||||
"body": {}
|
||||
},
|
||||
"actor": {
|
||||
"providerId": "example",
|
||||
"tenantId": "tenant-a",
|
||||
"subjectId": "ou_viewer",
|
||||
"credential": "signed-owner-verified-token"
|
||||
}
|
||||
},
|
||||
"required": ["id", "body", "actor"],
|
||||
"required": [
|
||||
"id",
|
||||
"body",
|
||||
"actor"
|
||||
],
|
||||
"optional": []
|
||||
}
|
||||
Reference in new issue
Block a user