Skip to content

SR-4 WI-2: ChannelMessage envelope API + sequence + transcript export #932

Description

@linear

What

Wrap L2PS messages in the DACS ChannelMessage envelope (DACS-3 §8.3.3) with monotonic per-channel sequencing, CCI-key signing, and transcript export. Satisfies CH-3 (authenticity) and CH-6 (per-session channelId uniqueness).

Envelope (verbatim from §8.3.3)

type ChannelMessage = {
    channelId: string
    sequence: number             // monotonic per channel, starts at 1
    sender: ClaimReference       // author's CCI primary claim (from WI-1)
    sentAt: number               // unix ms
    type: "offer" | "counter" | "accept" | "reject"
        | "sealed-envelope-commit" | "sealed-envelope-reveal" | "abort"
    body: unknown
    refs?: { repliesTo?: number }
    signature: ChannelMessageSignature
}

Signing rule (exact)

canonical_form := RFC 8785 JCS of envelope with `signature` omitted
envelope_hash  := sha256(canonical_form)  // hex
signed_bytes   := "dacs-channelmsg:v1:" || envelope_hash

Signed with the sender's Demos Ed25519 key (via signWithPrimaryClaim from WI-0). Transport-level fields may wrap the envelope but MUST NOT change the signed bytes.

Three things to implement

  1. Sequence numbering — assign + enforce monotonic per-channel sequence starting at 1. Reject out-of-order / duplicate sequences. Combined with CH-6 this is the anti-replay defence (§8.12).
  2. channelId uniqueness (CH-6) — substrate derives a per-session-unique channelId. Reject a session that reuses a prior channelId.
  3. Sign/verify + transcript export:
    • sign(envelope, demos) -> ChannelMessage
    • verify(msg) -> bool — verifies signature against the sender's bound CCI key (via WI-1 resolveMember), checks sender ∈ members, checks sequence monotonicity
    • exportTranscript(channelId, messages, members) -> ChannelTranscript:
type ChannelTranscript = {
    transcriptVersion: "1"
    channelId: string
    members: ClaimReference[]
    messages: ChannelMessage[]   // in sequence order
    generatedAt: number
    signatures: TranscriptSignature[]
}

Acceptance

  • Round-trip: construct → sign (Demos key, NOT RSA) → send → receiver verify() passes.
  • Reused channelId is rejected (CH-6).
  • Out-of-order sequence is rejected.
  • Duplicate sequence is rejected.
  • Full ordered transcript exports and can be re-verified message-by-message by a non-member given the public keys.
  • Negative: message from unbound sender (no WI-1 binding) is rejected.
  • Negative: message signed by RSA subnet key is rejected.

Dependencies

  • Blocked by WI-1 (resolveMember, binding registry).
  • Consumes WI-0 helpers for signing + verification.

Source

Brief §2 WI-2 + DACS-3 §8.3.3, §8.12.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions