channel

abstract fun channel(id: String): Seam

Returns a Seam view scoped to this channel id.

The returned Seam provides:

  • peers — the admitted roster plus self, reactive to MembershipEvent.Joined / MembershipEvent.Left. Raw transport peers that have not completed the admit handshake are never included.

  • incoming — frames from admitted members tagged with this channel id, with channel framing stripped. Frames from unadmitted peers are silently dropped.

  • broadcast / sendTo — send channel-framed payloads over the Room's underlying transport. No-ops when the room is terminal (HostLost / closed).

  • state — forwards the underlying us.tractat.kuilt.core.SeamState.

  • close — no-op. The Room owns lifecycle; closing the channel view does not tear down the Room.

Admit-gating guarantee for replicators

A us.tractat.kuilt.quilter.Quilter running over this Seam uses peers to maintain its membership book, so:

  • FullState (the convergence base) is sent via sendTo only to peers in peers — i.e. admitted members. An unadmitted transport peer never receives FullState and therefore cannot reconstruct the replicated state.

  • Ack and Resend are also sendTo gated on admitted peers.

  • Delta frames are broadcast via seam.broadcast and reach all connected transport peers, including unadmitted ones. Unadmitted peers have no FullState base to apply deltas to, so the frames are harmless noise. This is the documented behaviour: channel framing and admit-gated peers prevent the replicator from targeting unadmitted peers and from FullState-syncing them; they do not encrypt or withhold broadcast bytes at the wire level.

Wire framing

Channel frames are prefixed with RoomChannel.CHANNEL_PREFIX (0x63, 'c' for "channel") followed by a 2-byte sub-id derived deterministically from id via RoomChannel.channelSubId. This keeps frame headers small (3 bytes overhead) and requires no registration handshake — both peers independently compute the same sub-id for the same String. Sub-id collisions across distinct channel names are theoretically possible (1/65536 per pair) but negligible for typical usage (< 100 channels).

Applications must not emit payloads starting with RoomChannel.CHANNEL_PREFIX on the Room's raw broadcast / sendTo — that byte is reserved for channel framing.

Idempotency

Multiple calls with the same id return the same Seam instance.

Late-subscriber semantics

The shared upstream uses replay = 0. Frames emitted before incoming is collected are dropped. Safe for us.tractat.kuilt.quilter.Quilter (gaps heal via FullState + resend) but not for raw at-least-once consumers.