Files
meshcore-spec/src/08-payload-group.md
T
swaitsandClaude Opus 4.7 d785e35919 feat(mdbook): publish spec via mdBook on GitHub Pages
Set up the protocol spec as an mdBook and add a GitHub Actions
workflow that builds it on every push/PR and deploys to GitHub
Pages on main. The repo is now both a machine-readable corpus and
a browsable, searchable site.

Structure changes (one-time, mechanical):

- Rename the spec source directory from `spec/` to `src/` to match
  mdBook's default convention. All 21 chapter files move; their
  internal relative cross-links are unchanged.
- Add `src/SUMMARY.md` as the mdBook table of contents, grouping
  chapters under Wire Format, Payloads, Cryptography & Identity,
  and Routing & Higher-Layer Protocols.
- Add `book.toml` at repo root: title, author, search enabled,
  fold enabled, edit-on-GitHub links, navy theme.
- Add `.github/workflows/mdbook.yml`: build on push + PR, deploy
  on push to main only (configure-pages + upload-pages-artifact +
  deploy-pages from actions/).
- Add `/book/` to .gitignore.

Reference updates so the old path is fully retired:

- Every `spec_ref` in `corpus/**/*.json` rewritten from `spec/` to
  `src/` (54 files).
- Schema description text and example path in
  `corpus/schema/test-vector.schema.json`.
- `README.md`: prominent link to the published site near the top;
  "What's Here" updated for the new layout.
- `CONTRIBUTING.md`: spec-editing instructions point at `src/`
  and note the SUMMARY.md requirement.
- `CLAUDE.md`: directory-layout notes updated.

CHANGELOG.md historical entries intentionally keep their `spec/`
paths since those record what the paths were at the time of those
commits.

After this lands, the user needs to enable GitHub Pages in repo
settings with source = "GitHub Actions" (one-time, manual). The
workflow will then publish to https://swaits.github.io/meshcore-spec/
on every push to main.

Verified locally: `mdbook build` produces a clean book/ tree with
all 21 chapters rendered and the SUMMARY-driven sidebar populated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 09:36:28 -06:00

2.7 KiB
Raw Blame History

MeshCore Protocol Specification

Section 8: Payload — Group (GRP_TXT / GRP_DATA)

Overview

Group payloads enable encrypted communication within a channel. All members of a channel share a symmetric secret. The payload begins with a 1-byte channel hash (derived from the channel's shared key), followed by an encrypt-then-MAC blob.

Payload Types

Value Name Description
0x05 GRP_TXT Group text message
0x06 GRP_DATA Group datagram (binary data)

Wire Format

 0
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|channel_hash(1)|    Cipher MAC (2 bytes)       |               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               |
|              Ciphertext (N × 16 bytes, N ≥ 1)                |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Fields

Field Offset Size Type Description
channel_hash 0 1 byte raw First byte of SHA-256(channel_shared_key)
cipher_mac 1 2 bytes raw Truncated HMAC-SHA256 over ciphertext
ciphertext 3 variable (multiple of 16) raw AES-128-ECB encrypted data

Minimum Size

  • Minimum payload: 1 + 2 + 16 = 19 bytes

Channel Hash Derivation

The channel_hash is the first byte of the SHA-256 hash of the channel's 32-byte shared key (PUB_KEY_SIZE bytes):

channel_hash = SHA256(channel_shared_key)[0]

This means hash collisions are expected (1 in 256 chance). When a receiver finds a matching channel_hash, it attempts decryption with each matching channel's secret. Only a successful MAC verification confirms the correct channel.

Encryption

  1. Encrypt plaintext using AES-128-ECB with first 16 bytes of the channel's shared key (GroupChannel.secret).
  2. Compute HMAC-SHA256 over ciphertext using all 32 bytes of the channel's shared key. Truncate to 2 bytes.
  3. Assemble: channel_hash || MAC || ciphertext.

Decryption

  1. Read channel_hash (1 byte).
  2. Search local channel database for channels with matching hash. Multiple channels may match (up to 4 in reference implementation).
  3. For each matching channel, attempt MAC verification and decryption using the channel's shared key.
  4. The first channel that produces a valid MAC yields the correct decryption.

Cross-References

Reference Implementation

  • Mesh::createGroupDatagram() in src/Mesh.cpp — Encoding
  • Mesh::onRecvPacket(), cases PAYLOAD_TYPE_GRP_TXT/GRP_DATA — Decoding
  • Mesh::searchChannelsByHash() — Channel lookup