feat(group-messaging): add @bsv/group-messaging - #546
Draft
kjartan221 wants to merge 2 commits into
Draft
kjartan221 wants to merge 2 commits into
kjartan221 wants to merge 2 commits into
Conversation
End-to-end encrypted group messaging for BRC-100 wallets, with MLS (RFC 9420) group state by way of ts-mls. Each member's MLS credential carries a wallet attestation derived against the "anyone" root, so every member verifies every other independently and a peer cannot join under a name that is not theirs. The library opens no database and no network connection of its own: it takes the storage and transport the host already has. One socket is opened, on the host's own MessageBoxClient, and only when a caller sets `live: true`. Storage backends for Map, SQL and IndexedDB; transport backends for an in-process hub and @bsv/message-box-client. Live socket delivery is additive rather than an alternative, because `joinRoom` replays no backlog: a slower poll always runs underneath as the backstop that makes a silent socket recoverable. Private KeyPackage material is never stored. `keyPackages.create()` returns the private half once and keeps only the public one; a successful join or create retires the spent ref and emits `keyPackageConsumed` so the host knows which secret to destroy. Migrated from a standalone repository. History was not carried across: the reasoning lives in code comments and the README rather than in commit messages, and the source history is interleaved with a demo application that did not move. That demo, which exercises this package end to end over a live MessageBox socket and against an adversary suite, stays there. 413 tests, typecheck, lint and format:check all pass. Registered as node-library rather than browser-library: the package runs in the browser and imports no node builtins, but certifying that profile needs a measured browser-budget.json, and check-browser-package.mjs cannot run on Windows. LICENSE.txt, LICENSES/ and THIRD_PARTY_NOTICES.md are not included and are the one remaining repository-health finding.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Three groups. The blockers were three tests with no runtime assertion in wire-payload.test.ts. They were compile-fail fixtures — each `@ts-expect-error` breaks the build when WirePayload's brand is relaxed — but the brand is erased once compiled, so spec §4.1 held only for TypeScript callers and one of those tests asserted that the private KeyPackage was in fact sent. Every payload the library emits comes from encodeEnvelope, so send and broadcast now refuse anything that is not a well-formed envelope: a private KeyPackage reads as envelope kind 0, a bare KeyPackage as version 0, and one rule covers both without sniffing for key material. The tests assert the rejection and were watched to fail without it. The three cognitive-complexity findings are decomposed by responsibility, not split to satisfy a counter. MessageBoxTransport.#deliver gives up #settled, #decode and #fanOut while keeping its own control flow, because the group vanishing mid-drain has to abandon the batch rather than write it back. InviteService.handle becomes one method per message type with the six identical refusals behind #refuse. The rest are cosmetic: a useless constructor, a nested ternary, \w, code-point string methods, one readonly, and five length comparisons as toHaveLength. The six redundant-type-alias findings are left alone. IdentityKey, ChatId and their siblings are documentation aliases, and branding them is a public API decision to take deliberately rather than in response to a linter.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



End-to-end encrypted group messaging for BRC-100 wallets, with MLS (RFC 9420) group state by way of ts-mls. Each member's MLS credential carries a wallet attestation derived against the "anyone" root, so every member verifies every other independently and a peer cannot join under a name that is not theirs.
The library opens no database and no network connection of its own: it takes the storage and transport the host already has. One socket is opened, on the host's own MessageBoxClient, and only when a caller sets
live: true.Storage backends for Map, SQL and IndexedDB; transport backends for an in-process hub and @bsv/message-box-client. Live socket delivery is additive rather than an alternative, because
joinRoomreplays no backlog: a slower poll always runs underneath as the backstop that makes a silent socket recoverable.Private KeyPackage material is never stored.
keyPackages.create()returns the private half once and keeps only the public one; a successful join or create retires the spent ref and emitskeyPackageConsumedso the host knows which secret to destroy.Migrated from a standalone repository. History was not carried across: the reasoning lives in code comments and the README rather than in commit messages, and the source history is interleaved with a demo application that did not move. That demo, which exercises this package end to end over a live MessageBox socket and against an adversary suite, stays there.
413 tests, typecheck, lint and format:check all pass.
LICENSE.txt, LICENSES/ and THIRD_PARTY_NOTICES.md are not included and are the one remaining repository-health finding.
Program and scope
messagingarea@bsv/sdkgives wallets identity and payment but nogroup messaging primitive. This adds one: MLS (RFC 9420) group state via
ts-mls, with each member's MLS credential carrying a BRC-100 walletattestation derived against the "anyone" root, so every member verifies every
other independently and nobody can join under a name that is not theirs.
Developed in a standalone repository; this is the migration, a file copy rather
than a history graft.
adversary suite, simulation scenarios) stays in the standalone repository.
LICENSE.txt, theLICENSES/entry and theTHIRD_PARTY_NOTICES.mdrow are deliberately absent and deliberately notbaselined away:
scripts/repository-health.mjsreports exactly one finding,missing-license-file, and it is meant to stay visible. It needs a maintainerdecision on which Open BSV variant applies, not a guess.
browser-libraryprofile. Registered asnode-librarywithnode-cjsand
node-esmconsumer profiles. The code has no Node-only dependency and anIndexedDB backend ships in it, but that profile carries a
browser-budget.jsonand there is no measured number to put in it yet.Follow-up, not a fabricated budget.
83f052faac8d420be0c9a8933802dc25ed794ba3Impact
Affected packages/services and intended patch versions:
@bsv/group-messaging— new,0.1.0, unpublished. No existing package'ssource or manifest is touched. The non-package files in the diff are
docs/packages/messaging/group-messaging.md,governance/repository-health/projects.json,governance/repository-health/baselines.json,governance/package-release-notes.jsonandpnpm-lock.yaml.Shipped deliberately caveated. The README opens with a
Security statussectionrather than burying one at the bottom, and the docs page is registered
status: experimental, notbeta:0.x,ts-mlscarries its ownno-formal-audit disclaimer and that inherits, the attestation layer on top has
been reviewed by reading rather than audited, and there has been no interop
testing against another MLS implementation. If the program would rather that
caveat be worded differently or live elsewhere, say so and it moves.
Security-sensitive boundary, named so review can find it: this library accepts
KeyPackages, Welcomes and Commits from unauthenticated peers. The check that
stops a peer joining under someone else's identity is in
src/mls/authentication.ts, andsrc/mls/state.tsis what re-attaches it torestored group state — without that line the check silently stops running.
src/bootstrap/envelope.tsis the wire parser.Verification
pnpm test(package) — 27 files, 421 tests, all greenpnpm typecheck— cleanoxlint src— cleanprettier --check— cleannode scripts/repository-health.mjs— one finding,packages/messaging/group-messaging::missing-license-file, deliberate (seescope), not baselined
attw --pack— ESM and CJS resolution clean in both directionschecks are terminal on this head.
ts-mls's and is not re-derivedhere. What this package's own suite proves is the layer above it — the wallet
attestation is refused at all three seams
ts-mlsexposes:createCommit,joinGroup, andprocessMessage'sapplyTreeMutations(
src/mls/__tests__/engine-membership-forgery.test.ts). All three cases werewatched to fail with
validateCredentialstubbed to returntrue.scripts/check-package-artifact.mjsand
scripts/check-browser-package.mjsboth die on this Windows workstationwith
spawn pnpm ENOENTbefore reaching any check.attw --packstood in forthe artifact half. Neither is real evidence until CI runs them.
browser-librarynoteabove.
compatibility, public API, artifacts, dependencies, docs, and operations
(draft — nothing hosted has run yet)
Security and dependencies
audit results were reviewed
CodeQL has not run on this head
(not run)
removal condition — none added; the one open finding is reported rather
than excepted
no workflow and no lifecycle script changed
Trust-boundary coverage, since that boundary is the reason the package exists.
Negative tests cover forged credentials at all three MLS seams; a KeyPackage
declaring an unsupported or mismatched ciphersuite; malformed and truncated wire
envelopes; oversized and null-typed content fields; unbounded writes by an
unauthenticated peer, bounded per group by both count and bytes; and handler
isolation, so one subscriber throwing cannot abort a delivery loop.
Dependency evidence
ts-mls@1.6.4is the MLS implementation and the reason the package can exist;@noble/ciphers@2.1.1and@noble/curves@2.0.1are its peer requirements.1.6.4 over 1.6.2 is not cosmetic:
needsUpdatePathnow forces a path updatefor a single Remove where 1.6.2 did not — a post-compromise-security fix — and
getCommitSecretreplaces aderiveSecret(secret, "path", kdf)call in the keyschedule, so 1.6.2 and 1.6.4 peers are not expected to interoperate. Pinned
exactly rather than caretted for that reason. The credential-binding seams were
re-read against 1.6.4 and the
authServicecall sites are unchanged from 1.6.2.@bsv/sdkis a peer at^2.4.1, not a dependency.@bsv/message-box-clientis a devDependency only —the transport backend types against it; the host supplies the instance.
pnpm-lock.yamlregenerated in-repo,ts-mls@1.6.4resolves once.
attw --packfor the twoconsumer profiles.
Release and operations
required, no existing package is touched
apply, library only
README.md,CHANGELOG.md,AGENTS.md,docs/packages/messaging/group-messaging.mdand agovernance/package-release-notes.jsonentryCompletion evidence
guidance are current or concretely not applicable
handed to another contributor as "complete" — the two gaps are named above
rather than left for a reviewer to find: the licence artefacts and the
browser-libraryprofileis assumed