You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#2114 is not safe to apply to an environment until that environment's bucket is migrated. It repairs binary reads that never worked and simultaneously breaks binary objects that did work. Staging is already migrated. Production is not.
What is in the buckets
Before #2114, Store.write_bytes had no S3 implementation, so it fell back to data.decode('latin-1') → write_file → .encode('utf-8'). Every byte above 0x7F became two. Objects written that way are stored expanded.
\xc2\x89 is the UTF-8 encoding of 0x89. Inflation ~1.5x.
The old, apparently-broken read path handled these correctly. Expanded data is valid UTF-8, so read_file decoded it and .encode('latin-1') returned the original bytes. The round trip was self-consistent.
After #2114, read_bytes returns the stored bytes verbatim — the expanded ones. They are served through shell.py:366 and app_handler.py:150, so browsers receive corrupt fonts and images.
Measured, staging
104 of 778 objects, all under the system org, in the built-in apps:
app
objects
rocketride.pipeBuilder
80
rocketride.home
12
rocketride.benchmark
8
rocketride.aparavi
4
By type: 43 woff2, 41 woff, 19 png, 1 gif.
Staging migrated and verified — full rescan, 778 objects, zero remaining expanded.
Detecting these is harder than it looks — two traps
Magic bytes do not work for fonts.wOF2 and wOFF are ASCII, so they survive the expansion unchanged. A classifier keyed on the signature reports an expanded font as healthy. My first pass found 18 of 104 for exactly this reason.
Use the length field instead: WOFF and WOFF2 both carry total file size as a big-endian uint32 at offset 8. Expanded ⇒ declared ≠ actual.
Extensions lie.install.png is a JPEG. Sniff content, not the name.
Safe procedure per environment
Enumerate objects; skip pure-ASCII (latin-1 and UTF-8 agree — indifferent)
Keep only those that decode as UTF-8 with all codepoints < 256
Unwrap: data.decode('utf-8').encode('latin-1')
Write back only if the unwrapped bytes validate in the format's own terms — WOFF length field, PNG 8-byte signature, JPEG SOI+EOI. Never on a guess.
Rescan to confirm zero remain
Step 4 is what makes this safe to re-run: an already-migrated object fails the unwrap check and is left alone.
Origin
I asserted in #2114 that no existing object changes meaning, on the evidence of a single sampled remoteEntry.js that happened to be pure ASCII. That was a sample reported as a population.
Warning
#2114 is not safe to apply to an environment until that environment's bucket is migrated. It repairs binary reads that never worked and simultaneously breaks binary objects that did work. Staging is already migrated. Production is not.
What is in the buckets
Before #2114,
Store.write_byteshad no S3 implementation, so it fell back todata.decode('latin-1')→write_file→.encode('utf-8'). Every byte above0x7Fbecame two. Objects written that way are stored expanded.\xc2\x89is the UTF-8 encoding of0x89. Inflation ~1.5x.The old, apparently-broken read path handled these correctly. Expanded data is valid UTF-8, so
read_filedecoded it and.encode('latin-1')returned the original bytes. The round trip was self-consistent.After #2114,
read_bytesreturns the stored bytes verbatim — the expanded ones. They are served throughshell.py:366andapp_handler.py:150, so browsers receive corrupt fonts and images.Measured, staging
104 of 778 objects, all under the system org, in the built-in apps:
rocketride.pipeBuilderrocketride.homerocketride.benchmarkrocketride.aparaviBy type: 43
woff2, 41woff, 19png, 1gif.Staging migrated and verified — full rescan, 778 objects, zero remaining expanded.
Detecting these is harder than it looks — two traps
Magic bytes do not work for fonts.
wOF2andwOFFare ASCII, so they survive the expansion unchanged. A classifier keyed on the signature reports an expanded font as healthy. My first pass found 18 of 104 for exactly this reason.Use the length field instead: WOFF and WOFF2 both carry total file size as a big-endian
uint32at offset 8. Expanded ⇒ declared ≠ actual.Extensions lie.
install.pngis a JPEG. Sniff content, not the name.Safe procedure per environment
data.decode('utf-8').encode('latin-1')Step 4 is what makes this safe to re-run: an already-migrated object fails the unwrap check and is left alone.
Origin
I asserted in #2114 that no existing object changes meaning, on the evidence of a single sampled
remoteEntry.jsthat happened to be pure ASCII. That was a sample reported as a population.🤖 Generated with Claude Code
https://claude.ai/code/session_015nTVr6jfSFYm1GppxbjghP