A consistency follow-up from the provider-code envelope work in #1043, filed now so it is not lost; nothing shipped today can hit it.
The asymmetry
On both mint paths (organic generation and the client-cycle resolve), the identity-graph row is written under the provider-coded key with the provider's canonical value form inside the envelope (provider_kv_key, which calls the provider's normalize_id_for_kv on the value part). Reads (identify, EID resolution) and withdrawal tombstones use the raw in-use identifier from the cookie as the key.
For every provider shipped today these are the same string: the HMAC and host-signal providers use the default normalization, which lowercases a hash their generator already emits in lowercase, and the demo provider normalizes to identity. The asymmetry only bites a future provider whose canonical form differs from its minted form, for example one that case-folds: its rows would be written under a key its own reads and tombstones never produce, so a withdrawal marker could miss the row it is meant to kill.
Direction
Withdrawal and tombstoning are core-only operations on the key in use; they are not provider surface, and this fix must keep them that way. Two options, to be decided when the first canonicalizing vendor value is real:
- Route every graph access (reads, writes, tombstones) through one keying helper, so the canonical form is applied uniformly.
- Drop value normalization from the write path entirely, since the provider-code envelope now provides the namespacing that normalization partly existed for, and require providers to mint in canonical form.
Option 2 is simpler and removes the trap rather than managing it, but it changes the normalize_id_for_kv contract, so it belongs with the vendor-provider work rather than a hotfix.
Produced with AI assistance under James Rosewell's direction; reviewed before filing.
A consistency follow-up from the provider-code envelope work in #1043, filed now so it is not lost; nothing shipped today can hit it.
The asymmetry
On both mint paths (organic generation and the client-cycle resolve), the identity-graph row is written under the provider-coded key with the provider's canonical value form inside the envelope (
provider_kv_key, which calls the provider'snormalize_id_for_kvon the value part). Reads (identify, EID resolution) and withdrawal tombstones use the raw in-use identifier from the cookie as the key.For every provider shipped today these are the same string: the HMAC and host-signal providers use the default normalization, which lowercases a hash their generator already emits in lowercase, and the demo provider normalizes to identity. The asymmetry only bites a future provider whose canonical form differs from its minted form, for example one that case-folds: its rows would be written under a key its own reads and tombstones never produce, so a withdrawal marker could miss the row it is meant to kill.
Direction
Withdrawal and tombstoning are core-only operations on the key in use; they are not provider surface, and this fix must keep them that way. Two options, to be decided when the first canonicalizing vendor value is real:
Option 2 is simpler and removes the trap rather than managing it, but it changes the
normalize_id_for_kvcontract, so it belongs with the vendor-provider work rather than a hotfix.Produced with AI assistance under James Rosewell's direction; reviewed before filing.