release(v4.6.0): broadcast channels — Signal sender-keys for one-to-many fan-out
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
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>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@shade/crypto-web",
|
||||
"version": "4.5.0",
|
||||
"version": "4.6.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { StorageProvider, IdentityKeyPair, SignedPreKey, OneTimePreKey, SessionState, RetiredIdentity, PersistedStreamState, PeerVerification } from '@shade/core';
|
||||
import type {
|
||||
StorageProvider, IdentityKeyPair, SignedPreKey, OneTimePreKey,
|
||||
SessionState, RetiredIdentity, PersistedStreamState, PeerVerification,
|
||||
BroadcastChannelRecord, BroadcastMemberRecord,
|
||||
} from '@shade/core';
|
||||
import { constantTimeEqual } from '@shade/core';
|
||||
|
||||
/**
|
||||
@@ -167,4 +171,62 @@ export class MemoryStorage implements StorageProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Broadcast channels (V4.6) ────────────────────────────
|
||||
|
||||
private broadcastChannels = new Map<string, BroadcastChannelRecord>();
|
||||
private broadcastMembers = new Map<string, Map<string, BroadcastMemberRecord>>();
|
||||
|
||||
async saveBroadcastChannel(channel: BroadcastChannelRecord): Promise<void> {
|
||||
this.broadcastChannels.set(channel.channelId, cloneChannel(channel));
|
||||
}
|
||||
|
||||
async getBroadcastChannel(channelId: string): Promise<BroadcastChannelRecord | null> {
|
||||
const v = this.broadcastChannels.get(channelId);
|
||||
return v ? cloneChannel(v) : null;
|
||||
}
|
||||
|
||||
async listBroadcastChannels(): Promise<BroadcastChannelRecord[]> {
|
||||
return [...this.broadcastChannels.values()].map(cloneChannel);
|
||||
}
|
||||
|
||||
async removeBroadcastChannel(channelId: string): Promise<void> {
|
||||
this.broadcastChannels.delete(channelId);
|
||||
this.broadcastMembers.delete(channelId);
|
||||
}
|
||||
|
||||
async saveBroadcastMember(member: BroadcastMemberRecord): Promise<void> {
|
||||
let inner = this.broadcastMembers.get(member.channelId);
|
||||
if (!inner) {
|
||||
inner = new Map();
|
||||
this.broadcastMembers.set(member.channelId, inner);
|
||||
}
|
||||
inner.set(member.peerAddress, { ...member });
|
||||
}
|
||||
|
||||
async getBroadcastMembers(channelId: string): Promise<BroadcastMemberRecord[]> {
|
||||
const inner = this.broadcastMembers.get(channelId);
|
||||
return inner ? [...inner.values()].map((m) => ({ ...m })) : [];
|
||||
}
|
||||
|
||||
async removeBroadcastMember(channelId: string, peerAddress: string): Promise<void> {
|
||||
this.broadcastMembers.get(channelId)?.delete(peerAddress);
|
||||
}
|
||||
}
|
||||
|
||||
function cloneChannel(c: BroadcastChannelRecord): BroadcastChannelRecord {
|
||||
const out: BroadcastChannelRecord = {
|
||||
channelId: c.channelId,
|
||||
ownerRole: c.ownerRole,
|
||||
ownerAddress: c.ownerAddress,
|
||||
generation: c.generation,
|
||||
chainKey: new Uint8Array(c.chainKey),
|
||||
iteration: c.iteration,
|
||||
signingPublicKey: new Uint8Array(c.signingPublicKey),
|
||||
createdAt: c.createdAt,
|
||||
updatedAt: c.updatedAt,
|
||||
};
|
||||
if (c.label !== undefined) out.label = c.label;
|
||||
if (c.signingPrivateKey !== undefined) out.signingPrivateKey = new Uint8Array(c.signingPrivateKey);
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user