Some checks failed
Test / test (push) Has been cancelled
Cross-platform vectors / TypeScript vectors (bun) (push) Has been cancelled
Cross-platform vectors / Kotlin vectors (gradle) (push) Has been cancelled
Docker build and publish / docker (push) Has been cancelled
Publish / publish (push) Has been cancelled
Lands the broadcast-channel primitive Prism asked for in Docs/shade-feature-request-sender-keys.md. The crypto in @shade/core/sender-keys.ts was already in place; this release wires it up as a first-class app-facing API, adds the persistence schema across all six storage backends (memory, sqlite, indexeddb + encrypted variants), introduces wire type 0x21 in @shade/proto, and ships Prism's three acceptance tests verbatim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
557 lines
19 KiB
TypeScript
557 lines
19 KiB
TypeScript
/**
|
||
* V4.6 — Broadcast channels for one-to-many fan-out.
|
||
*
|
||
* Wraps the Signal-style sender-key primitives (`@shade/core/sender-keys`)
|
||
* in a persistent, app-friendly handle. The crypto is unchanged from
|
||
* `sender-keys.ts`; this module wires it into:
|
||
*
|
||
* - `StorageProvider.{save,get,list,remove}BroadcastChannel` for state
|
||
* - `Shade.send` for distribution (sealed by the bilateral ratchet)
|
||
* - `@shade/proto.encodeBroadcast` for the on-wire broadcast envelope
|
||
*
|
||
* The model is **strictly one-to-many**: the channel owner is the only
|
||
* sender. Members hold a tracking copy of the chain key (no signing
|
||
* privkey) and can decrypt — they cannot send into the channel. This
|
||
* matches the Prism use case (PC desktop fans output frames out to
|
||
* paired peers); a future symmetric-group API would build on the same
|
||
* persistence layer.
|
||
*/
|
||
|
||
import {
|
||
type CryptoProvider,
|
||
type StorageProvider,
|
||
type BroadcastChannelRecord,
|
||
type BroadcastMemberRecord,
|
||
type GroupSession,
|
||
type SenderKeyState,
|
||
buildDistribution,
|
||
createSenderKey,
|
||
installDistribution,
|
||
senderKeyEncrypt,
|
||
senderKeyDecrypt,
|
||
toBase64,
|
||
fromBase64,
|
||
} from '@shade/core';
|
||
import {
|
||
encodeBroadcast,
|
||
decodeBroadcast,
|
||
inspectEnvelopeType,
|
||
type BroadcastWire,
|
||
} from '@shade/proto';
|
||
import type { ShadeEnvelope } from '@shade/core';
|
||
|
||
/**
|
||
* Magic prefix used to embed broadcast control messages inside a regular
|
||
* bilateral plaintext (so they ride the existing `shade.send` /
|
||
* `shade.receive` path without a new ratchet wire type). The leading
|
||
* NULs make a collision with user-app text astronomically unlikely.
|
||
*/
|
||
const CONTROL_MAGIC = ' |