Political World — migrating addon data from sanitized keys to UTF-8 hex
✅ Source verified migration 💾 Compatibility case study
Political World’s addon storage contains a concrete example of why apparently harmless string sanitization can become a persistence bug.
Legacy key family
Section titled “Legacy key family”The inspected bridge still defines:
ukiol_api_data_as the legacy API 1.1 addon-data prefix.
Legacy IDs were passed through a sanitizer before being concatenated into a key.
The problem with replacement-style sanitization is that different original strings can potentially map to the same sanitized representation.
v2 design
Section titled “v2 design”API 1.2+ introduced:
pw_api2_data_and constructs a key from UTF-8 bytes encoded as hexadecimal.
Conceptually:
pw_api2_data_+ HEX(UTF8(addonId))+ "_"+ HEX(UTF8(localKey))The source comment explicitly gives the intended distinction:
author.my-addonauthor.my_addonshould remain separate.
Why hex
Section titled “Why hex”Hex encoding produces save-key components using only:
0-9 A-Fwhile preserving the exact UTF-8 byte identity of the input.
It is not compact, but it is deterministic and collision-safe with respect to the encoded input bytes.
Lazy migration
Section titled “Lazy migration”The getter path implements a copy-forward migration:
read v2│├─ found → return│└─ missing ↓read legacy│├─ missing → fallback│└─ found ↓write same value to v2 ↓return valueThe old key is deliberately left untouched.
Why not delete legacy immediately
Section titled “Why not delete legacy immediately”Keeping old data during lazy migration provides safer behavior for:
- rollback to an older build;
- worlds opened by mixed development versions;
- debugging;
- incomplete migration coverage.
Cleanup can happen later, after a migration window, if it is ever needed.
Migration is performed on read
Section titled “Migration is performed on read”This design avoids scanning every kingdom and every possible addon key at startup.
Only data that is actually read is migrated.
Tradeoff:
unused old keys may remain indefinitelybut there is no expensive global migration pass.
General lesson
Section titled “General lesson”Never build a persistent key namespace by casually replacing punctuation unless you have proven the transformation is injective for your allowed identifiers.
If old data already exists:
introduce new encoding→ read new first→ fallback to old→ copy forward→ keep old during migration windowis a robust compatibility pattern.