Files
dsh_shenxian/src/net/relay/index.ts
T
admin 09ce76f3af feat(overlay): 内容块级寻址 + 实例逐步拉起 + 骨干选路 + 组密钥加密(序24–㉛ 累积同步)
代码
- 内容分发块级寻址:新增 src/net/relay/content/{chunker,store,runtime,source,peer,crypto}.ts
- 组密钥(C 档)确定性加密:AES-256-GCM,块 id β′ = sha256(密文) 前 32 hex;双 epoch 过渡窗口
- 实例生命周期:三处 teardown() 不再杀实例(local/remote/leased-spawner);启动认领 + TCP 探活判孤儿
- 骨干选路:jitter 选路 + endpoint-target;relay client/server/wire/identity/directory/rendezvous/switcher 调整
- 工作台 src/web/server.ts、src/worker/relay-tunnel.ts 装配与候选链观测

脚本与测试
- scripts/overlay-{probe,keyring,jitter}.cjs 更新
- 探针新增 OBS-21(每连接候选数)/ OBS-22(teardown 静态守卫 + 认领面)/ OBS-23(组密钥加密)
- 新增 test/{orchestrator-teardown,orchestrator-rehydrate,overlay-content,overlay-jitter}.test.mjs;relay 两例更新

文档
- 新增交接单:覆盖网络-序24-内容分发块级寻址 / 序25-实例逐步拉起 / 序26-骨干稳定选路与加密
- INDEX.md、交接单/README.md、skills/dsh-auto-handoff-chain/SKILL.md 同步

验收(零回归,2026-09-18 08:0x 复核)
- npm test           201 tests / 200 pass / 0 fail / 1 skipped
- overlay-failover-drill --scene all --table   12 PASS / 0 SKIP / 0 FAIL
- overlay-probe --table                        23 PASS / 0 SKIP / 0 FAIL (rc=0)
2026-09-18 08:08:51 +08:00

158 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 覆盖网络中继(relay)—— **自研、零新增依赖、零新增公网口**(传输方案 §10 定案)。
*
* ## 一段话说完它是什么
* worker 侧 `RelayClient` **只拨出**一条 wss;`RelayServer` **只绑回环**,为每个注册端口在
* `127.0.0.1` 上开一条监听;Manager 连那条回环口 ⇒ 字节经多路复用跑回 worker 本地端口。
* 对 Manager 而言地址形态与 sshd 版**完全同形**(`host:port`),所以换它是 env 级动作。
*
* ## 三条硬约束(来自实测,改动前先读)
* 1. **零新增公网口**:47 无本机防火墙(`nft INPUT policy accept`)⇒ 绑 `0.0.0.0` 即公网可达。
* 2. **worker 只拨出**:任何"在 worker 上开监听"的方案都让暴露面从 O(1) 变 O(N)。
* 3. **精确 ACK**:注册与开流都必须有确认帧,未确认即断并计数 —— 治的是 SSH 版"静默失败"的病根。
*
* @module dshs/net/relay
*/
export { RelayServer, RELAY_PATH, DEFAULT_RELAY_PORT, DEFAULT_AUTH_DEADLINE_MS, DEFAULT_AUTH_WINDOW_MS, DEFAULT_IDLE_TIMEOUT_MS, DEFAULT_HB_SEC, DEFAULT_MAX_STREAMS_PER_PORT, DEFAULT_QUEUE_MAX_BYTES, DEFAULT_PRESENCE_GRACE_MS, DEFAULT_PRESENCE_OFFLINE_DEBOUNCE_MS, DEFAULT_PRESENCE_BATCH_MS, DEFAULT_PRESENCE_TTL_FACTOR, DEFAULT_PRESENCE_SUB_MAX } from './server.js'
export type { RelayServerOptions, RelayStatus, PresenceEntry } from './server.js'
export { RelayClient, describeClientStatus, gracefulBurstMsDefault, openedChannelFailedTerminally, waitUpOnStatus } from './client.js'
export type { RelayClientOptions, RelayClientStatus, RelayClientState, WebSocketLike, WebSocketCtor, WaitUpStatusOptions, RelayPresenceEntry, RelayPresenceStatus, RelayPresenceState } from './client.js'
export { RelayRendezvous } from './rendezvous.js'
export type { RelayRendezvousOptions } from './rendezvous.js'
export { RelayDialer } from './dialer.js'
export type { RelayDialerOptions } from './dialer.js'
export { RelayFailoverSupervisor, relayFailoverThresholds } from './switcher.js'
export type {
RelayChannelHandle,
RelayFailoverDeps,
RelayFailoverStats,
RelayFailoverThresholds,
} from './switcher.js'
export { MuxDuplex } from './duplex.js'
export type { MuxDuplexOptions } from './duplex.js'
export { MUX, WS_CLOSE, acceptWebSocket, encodeMux, decodeMux, encodeJsonFrame, parseJsonPayload, WsConnection } from './wire.js'
export type { MuxFrame, MuxType, WsServerOptions } from './wire.js'
export { OPS_NETWORK, NAME_SEP, assertNetworkId, assertSameNetwork, describeDialers, isHostId, isNetworkId, logicalName, normalizeDialers, parseLogicalName, sameNetwork } from './network.js'
export { DIRECTORY_PATH, DIRECTORY_PAYLOAD_TAG, DIRECTORY_VERSION, DEFAULT_OVERLAY_SEED, buildDirectoryDocument, directoryPayload, directoryUrlFor, overlayEnvSeeds, overlayEnvTrustedKeys, parseDirectory, publicKeyFrom, publicRelayEntries, readCachedDirectory, resolveOverlayRelay, listOverlayRelayCandidates, signDirectory, toRelayUrl, verifyDirectory, writeCachedDirectory } from './directory.js'
export type { CachedDirectory, DirectoryVerdict, OverlayAddressSource, OverlayDirectory, OverlayRelayCandidates, OverlayRelayResolution, ResolveOverlayRelayOptions } from './directory.js'
export { keyEntryOf, loadKeysFile, parseKeysInline, normalizeKeyRecord, lookupKey, describeKeyEntry, assertKey } from './keys.js'
export type { RelayKeyEntry, RelayKeyMap } from './keys.js'
export {
IDENTITY_VERSION,
NODE_GRANT_TAG,
REVOCATION_TAG,
SIGNER_SET_TAG,
PROOF_TAG,
DEFAULT_NODE_KEY_FILE,
generateAuthorityKey,
generateNodeKey,
identityEnvRequire,
identityEnvTrustedRoots,
identityEnvTrustedSigners,
loadNodeGrant,
loadOrCreateNodeKey,
loadRevocations,
loadTrustedSigners,
nodeGrantPayload,
nodeKeyFingerprint,
normalizePublicKey,
parseNodeGrant,
parseRevocationList,
parseSignerSet,
proofPayload,
publicKeyOfPrivate,
readSignedFile,
revocationPayload,
signNodeGrant,
signProof,
signRevocations,
signSignerSet,
signerSetPayload,
verifyNodeGrant,
verifyPeerGrant,
verifyProof,
verifyRevocations,
verifySignerSet,
writeSignedFile,
} from './identity.js'
export type {
IdentityReason,
IdentityVerdict,
NodeGrant,
PeerVerifyContext,
RevocationList,
SignerSet,
} from './identity.js'
export { chooseNode, describeDecision, rankCandidates, scoreCandidate } from './placement.js'
export type { ChooseOptions, NodeCandidate, PlacementDecision, PlacementScore, PlacementWeights } from './placement.js'
export { relayEndpointTarget, hostNameIndex } from './endpoint-target.js'
export type { RelayEndpointDecision, RelayEndpointTargetInput } from './endpoint-target.js'
// ── 序㉔ 内容分发(块级内容寻址 · 同网段 peer 优先)────────────────────────────
// ⛔ 本段**只做导出**(交接单 §3.1:`index.ts` 改动仅限导出)。
// 模块职责:`chunker`(切分+哈希)→ `store`(内容寻址存储)→ `source`(源优先级链)
// → `peer`(同网段 peer 与分组隔离)。
export {
DEFAULT_BLOCK_SIZE,
BLOCK_ID_HEX_LEN,
blockIdOf,
contentIdOf,
isBlockId,
chunkify,
planOf,
reassemble,
} from './content/chunker.js'
export type { Chunk, ChunkedContent } from './content/chunker.js'
export { ContentStore, DEFAULT_MAX_BYTES } from './content/store.js'
export type { ContentStoreCounters, ContentStoreOptions } from './content/store.js'
export { ContentSourceChain, SOURCE_TIERS, DEFAULT_TIER_ORDER, emptySourceCounters } from './content/source.js'
export type {
SourceTier,
SourceHitCounters,
SourceFetchOutcome,
TierFetchResult,
TierFetcher,
ContentSourceChainOptions,
} from './content/source.js'
export { ContentPeerGroup, groupKeyOf, sameGroup, PEER_COUNTER_KEYS } from './content/peer.js'
export type {
PeerDeclaration,
PeerCounters,
PeerCounterKey,
ContentPeerGroupOptions,
} from './content/peer.js'
export { ContentRuntime } from './content/runtime.js'
export type { ContentRuntimeOptions, ContentSnapshot } from './content/runtime.js'
// 🆕 序㉘ · 单 B:组密钥加密(缺省不启用)—— 走**既有**验签链,⛔ 不新根、不新签名链。
export {
ContentCipher,
openContentCipher,
loadGroupKeyFile,
describeGroupKey,
keyIdOf,
verifyGroupKeyCredential,
parseGroupKeyCredential,
groupKeyCredentialPayload,
CONTENT_CRYPTO_COUNTER_KEYS,
DEFAULT_GROUP_KEY_FILE,
DEFAULT_EPOCH_GRACE_MS,
CONTENT_CIPHER_VERSION,
GROUP_KEY_TAG,
IV_LEN,
TAG_LEN,
KEY_LEN,
MIN_BLOB_LEN,
} from './content/crypto.js'
export type {
ContentCryptoCounters,
ContentCryptoCounterKey,
ContentCipherOptions,
GroupKeyFile,
GroupKeyEpochEntry,
GroupKeyCredential,
GroupKeyLoadReason,
GroupKeyLoadResult,
LoadGroupKeyOptions,
} from './content/crypto.js'