Skip to content

refactor(bench): measure the leaf image that would actually ship - #400

Open
bahdotsh wants to merge 1 commit into
feat/leaf-node-cratefrom
feat/harnesses-use-leaf-crate
Open

refactor(bench): measure the leaf image that would actually ship#400
bahdotsh wants to merge 1 commit into
feat/leaf-node-cratefrom
feat/harnesses-use-leaf-crate

Conversation

@bahdotsh

Copy link
Copy Markdown
Member

Stage 3 of the leaf payload crypto work, part D of four. Stacked on #399.

The measurement was pricing something unshippable

The footprint harness's leaf image drove mls-rs directly, so it linked the MLS calls and nothing around them: no envelope codec, no control-frame signing, no address derivation. It now runs offline-protocol-leaf, so the number covers the code a device actually runs.

Flash vs baseline vs protocol only
never-committing leaf, the shipping profile 435.7 KiB 434.6 KiB 339.7 KiB
rfc_compliant, X.509 included (upper bound) 447.6 KiB 446.5 KiB 351.7 KiB

The figure moved from 390.2 KiB to 435.7 KiB, a little over a quarter of a 1536 KiB xG24.

Worth being plain about how that relates to the 400 KiB in ADR 0021: that was a decision gate, set to answer whether MLS on a leaf node was viable at all before anything was built, and it did that job. It is not a budget the shipping image is being held to. The recovery lever recorded beside it is still worth more than the growth: about 111 KiB of this image is P-384 and P-256 arithmetic nothing uses, linked because the crypto provider keeps all four curves in one enum with no feature gating.

leaf-min is deleted, not fixed

It priced application messages with the resilience features off. offline-protocol-leaf requires all four mls-rs features, cargo unifies features, and so the variant silently began measuring the same bytes as leaf: two rows, one image, and a reader with no way to tell.

A row reporting a number for a configuration nobody can build is worse than no row. This is the second time a gate in this tree has gone vacuous without announcing it, which is why the removal is called out here rather than done quietly.

tools/mls-interop is deliberately not converted

My plan said this PR would move the harness's leaf side onto the crate. That premise does not hold, and I'd rather say so than force it through.

The harness operates at the MLS layer, trading key package bytes and MLS messages. LeafDevice operates at the frame layer. Converting the harness would force it to build signed control frames on the phone side too, and its phone is raw OpenMLS rather than the SDK engine, so that means a second implementation of the engine's frame building living inside a harness. That is the exact failure ADR 0022 names, and the exact way this harness acquired its copies last time.

The frame layer is covered instead by the leaf crate's own tests (#399), which run a real MlsManager phone against the device in one process. The harness keeps doing the job only it can do: two pinned MLS libraries meeting out of process. It still passes untouched, all ten steps plus the three step-0 negative controls.

The remaining duplication is two lines (unwrapping the MLS message to a bare key package), and both copies are exercised against a real OpenMLS phone, so a drift would fail somewhere rather than pass everywhere. The values that could drift silently, the backdate and the lifetime, already come from offline-protocol-sealed.

Verification

./tools/embedded-footprint/measure.sh                      green, table above
cargo clippy --release --locked --target thumbv8m.main-none-eabihf \
    (base / --features leaf / --features leaf-full)        clean
cd tools/mls-interop && cargo run --release --locked       PASS, unchanged

The CI clippy loop drops leaf-min alongside the feature itself, so the job lints exactly the variants that exist.

@bahdotsh
bahdotsh force-pushed the feat/harnesses-use-leaf-crate branch from 7b9e938 to c80fd78 Compare August 21, 2026 17:21
The footprint harness's leaf image drove mls-rs directly, which meant it
linked the MLS calls and nothing around them: no envelope codec, no
control-frame signing, no address derivation. It priced an image nobody
could ship. It now runs `offline-protocol-leaf`, so the number covers the
code a device runs.

The figure moves from 390.2 KiB to 435.7 KiB of flash, a little over a
quarter of a 1536 KiB xG24. That is the honest number and it is worth being
plain about how it relates to the 400 KiB in ADR 0021: that was a decision
gate, set to answer whether MLS on a leaf node was viable at all before
anything was built, and it did that job. It is not a budget the shipping
image is being held to. The recovery lever recorded beside it is still
worth more than the growth: about 111 KiB of this image is P-384 and P-256
arithmetic nothing uses, linked because the provider keeps four curves in
one enum with no feature gating.

The `leaf-min` image is deleted rather than fixed. It priced application
messages with the resilience features off, and the crate requires all four
mls-rs features, so cargo's unification made the variant measure the same
bytes as `leaf`: two rows, one image, and a reader with no way to tell. A
row reporting a number for a configuration nobody can build is worse than
no row, and this is the second time a gate in this tree has gone vacuous
without saying so.

`tools/mls-interop` is deliberately NOT converted, and the plan that said it
would be was working from a premise that does not hold. The harness operates
at the MLS layer, trading key package bytes and MLS messages; `LeafDevice`
operates at the frame layer. Converting it would force the harness to build
signed control frames on the PHONE side too, and its phone is raw OpenMLS
rather than the SDK engine, so that means a second implementation of the
engine's frame building living in a harness. That is the exact failure ADR
0022 names, and the exact way this harness acquired its copies last time.
The frame layer is covered instead by the leaf crate's own tests, which run
a real MlsManager phone against the device in one process; the harness keeps
doing the job only it can do, which is two pinned MLS libraries meeting out
of process.
@bahdotsh
bahdotsh force-pushed the feat/harnesses-use-leaf-crate branch from c80fd78 to 9720df7 Compare August 21, 2026 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant