Skip to content

docs(core): scope the unit-identifier scrub to what it actually removes - #670

Open
yuzi-co wants to merge 1 commit into
AprilNEA:masterfrom
yuzi-co:docs/unit-identifier-scrub-scope
Open

docs(core): scope the unit-identifier scrub to what it actually removes#670
yuzi-co wants to merge 1 commit into
AprilNEA:masterfrom
yuzi-co:docs/unit-identifier-scrub-scope

Conversation

@yuzi-co

@yuzi-co yuzi-co commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

DeviceIdentity::without_unit_identifiers is documented as:

Remove per-unit identifiers before this model snapshot is persisted.

It removes them from the identity body. The saved file still names them, because a device's configuration key has to be stable and unique per physical unit and DeviceStableId::runtime_key embeds an identifier directly:

Self::Direct { vendor_id, product_id, identity }
    => format!("direct:{vendor_id:04x}:{product_id:04x}:{}", identity.key()),
// DeviceIdentity::key()
Self::Serial(serial) => format!("serial:{serial}"),
Self::Unit(unit)     => format!("unit:{}", hex_unit(*unit)),

So a real config contains, for one mouse:

selected_device = "direct:046d:c08d:unit:<unit id>"

[devices."direct:046d:c08d:unit:<unit id>".identity.model_info]
unit_id = [0, 0, 0, 0]

The field is blanked; the same four bytes appear in hex in the table name and again in selected_device. On the config I checked this against, the device's unit id occurs 5 times in the file and the receiver's UID 6 times.

raw: keys carry a route identity rather than a device identity. runtime_key writes whatever DeviceRoute::RawHid::identity holds, and transport.rs::device_identity builds that as serial:<serial> when the node reports one and id:<os node> otherwise. Persistence filters through raw_identity_is_physical, which accepts serial: and stable: and rejects the transient id: form - so a persisted raw: key does write a real serial number into the key while without_unit_identifiers clears model_info.serial_number.

Where the scrub does work

receiver: keys. receiver:<uid>:slot:<n> names the receiver and pairing slot, not the paired device, so clearing the body genuinely keeps the device's identifiers out of the file. (The receiver's own UID is still there.)

That is also the only shape the scrub is exercised against:

  • persisted_identity_strips_per_unit_identifiers uses receiver:test:slot:1
  • docs/config.example.toml contains receiver:aabbccdd:slot:N keys exclusively - no direct: or raw: example anywhere

The tested shape and the documented shape are both the shape where the scrub is effective. The shapes where it is a no-op are neither.

Change

Doc comment only. It now states that the body is stripped, and that the config key still carries an identifier - the device's own serial number or unit id for direct: and unknown: keys, an OS-node- or serial-derived route identity for raw: keys, and the receiver's UID for receiver: keys - with receiver: the one shape where stripping is effective. No behavior change, no signature change.

Not addressed here

Whether the current split is the intended privacy posture is a maintainer call, and any alternative (hashing the identifier into the key) would invalidate every existing config. This PR only stops the comment from overstating the guarantee. Related: #667, which proposes correlating a receiver-keyed entry with a direct-keyed entry for the same physical device - model_info.unit_id is the natural correlator and is zeroed at this boundary, so that fix cannot read it from disk.

Provenance

Introduced in #604 (fix(config): harden schema and persistence). That PR's description does not mention unit identifiers, serials, or privacy.

Testing

Rebased onto master (df5b7c8) and re-run on the rebased tip:

  • cargo fmt --all -- --check - clean
  • cargo clippy --workspace --all-targets -- -D warnings - clean
  • cargo test --workspace - clean
  • RUSTDOCFLAGS="-D warnings" cargo doc -p openlogi-core --no-deps --document-private-items - 5 unresolved-link errors, all pre-existing in the binding module and identical on master; the new runtime_key intra-doc link resolves
  • RUSTDOCFLAGS="-D warnings" cargo doc -p openlogi-hid -p openlogi-hidpp -p openlogi-hidpp-derive --no-deps --document-private-items - fails on the pre-existing AsyncHidChannel::supports_short_long_hidpp link in openlogi-hid/src/transport.rs, a file this PR does not touch; green with docs(hid): stop linking a cfg-gated type so rustdoc passes on Windows #661 cherry-picked on top
  • Verified on Windows 11 against a G502 LIGHTSPEED on both its Lightspeed receiver and USB cable

@greptile-apps

greptile-apps Bot commented Aug 19, 2026

Copy link
Copy Markdown

Greptile Summary

The PR narrows the documentation for DeviceIdentity::without_unit_identifiers to explain that only the identity body is scrubbed.

  • Clarifies which identifiers remain embedded in direct:, unknown:, raw:, and receiver: configuration keys.
  • Corrects the prior characterization of raw-HID keys by describing their identity as OS-node- or serial-derived.
  • Adds a resolved intra-doc link to DeviceStableId::runtime_key.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
crates/openlogi-core/src/config/device.rs Documentation-only clarification accurately scopes identifier scrubbing and resolves the previously reported description of raw-HID keys.

Reviews (2): Last reviewed commit: "docs(core): scope the unit-identifier sc..." | Re-trigger Greptile

Comment thread crates/openlogi-core/src/config/device.rs Outdated
`DeviceIdentity::without_unit_identifiers` was documented as removing
per-unit identifiers before a model snapshot is persisted. It removes
them from the identity body only. The configuration key has to be stable
and unique per physical unit, so `DeviceStableId::runtime_key` embeds an
identifier directly: `direct:` and `unknown:` keys carry the device's own
serial number or unit id, `raw:` keys carry an OS-node- or serial-derived
route identity, and a `receiver:` key carries the receiver's UID. A saved
config therefore still names the identifier the doc claimed had been
removed.

Stripping the body is only effective for `receiver:` keys, where the key
names the receiver and pairing slot rather than the paired device. That
is also the only key shape the scrub is exercised against: the
`persisted_identity_strips_per_unit_identifiers` test uses
`receiver:test:slot:1`, and `docs/config.example.toml` contains
`receiver:` keys exclusively.

Comment text only; no behavior change.
@yuzi-co
yuzi-co force-pushed the docs/unit-identifier-scrub-scope branch from f900af7 to be61ab3 Compare August 19, 2026 15:59
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