[GH-ISSUE #7038] [Feature] Read Hermes bytecode bundles: header, integrity, function and string tables #4258

Open
opened 2026-10-31 00:48:11 +00:00 by bsakaguchi · 12 comments
Owner

Originally created by @KJLavender on GitHub (Oct 24, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7038

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Evidence, artifacts, or reconstruction

Current limitation

React Native apps usually ship their JavaScript as Hermes bytecode (assets/index.android.bundle in an APK, a .jsbundle/.hbc in iOS apps). Right now REA only recognizes React Native by native library names (libhermes.so, hermes.framework, see docs/android-analysis.md). The bundle itself is opaque: no version, no strings, no function names.

#3608 covers this as part of a much bigger React reconstruction RFC. This is a proposal for a small first slice that doesn't depend on any of the rest: read the Hermes container itself, without disassembly or decompilation.

Use cases

The first questions on a React Native app are usually "which servers does it talk to", "what are the API paths", "is there a hardcoded key", and "roughly how is the code organized". Almost all of that is in the Hermes string table and function table, which can be read statically.

As a check I ran a rough local reader on the public BlueWallet 8.0.2 APK (open source, Hermes bytecode v98, 14 MB bundle). The string table alone shows its Electrum servers (electrum2.bluewallet.io:444, electrum.blockstream.info:60004), the price APIs it calls, and 26,262 named functions out of 40,594, including the Metro runtime (metroRequire, loadModuleImplementation, ...).

Desired outcome and success criteria

For a Hermes bytecode file, either standalone or inside an APK/IPA inventory:

  1. Header facts: bytecode version, source hash, declared file length, counts (functions, strings, identifiers, regexps, BigInts, CommonJS modules), option flags.
  2. Integrity: whether the SHA-1 footer matches the bytes before it.
  3. Function table: index, name (string id + text), parameter count, bytecode size and offset, kind (normal/generator/async/arrow), including the "overflowed" large headers.
  4. String table: every string with its kind (string/identifier), encoding (Latin-1 or UTF-16) and file offset, including the overflow table.
  5. Unsupported versions or delta-format files report a clear limitation instead of guessing.

Non-goals for this slice: disassembly, decompilation, Metro module splitting, writing anything.

Expected interaction with targets

Read existing files or captured data

Target and scope details (optional)

Bytecode versions differ a lot (the small function header went from 16 to 12 bytes with a different bit layout around v97/v98, BigInt fields arrived in v87, etc.). I'd suggest starting with the versions current React Native ships (v96–v99) plus the older v83/v84 if it's cheap, and refusing others explicitly.

Verification available so far

I have a prototype or observed results

Verification details (optional)

Local prototype (~250 lines, Node only), compared field by field with hermes-dec 0.1.8 on Windows:

Input Result
Same ES5 program compiled by hermesc 0.8.0, 0.11.0 and two 1.0.0 builds (bytecode v83, v84, v98, v99) header, 39/39 strings, 6/6 functions identical
Program with class/async/BigInt (v99) identical, async and generator functions labelled
Stress file: 802 functions, 3,204 strings, one 197 KB function, all four versions identical; covers overflowed headers (all of them on v98/v99, the huge function on v83/v84)
BlueWallet 8.0.2 index.android.bundle (v98, 14 MB) 40,594/40,594 functions and 109,319/109,320 strings identical, read in about 0.16 s

The one difference on BlueWallet looks like a hermes-dec bug rather than mine: string #46390 is a single U+FEFF stored as UTF-16 (ff fe), and hermes-dec decodes it with Python's utf-16 codec, which drops it as a BOM and returns an empty string.

Every sample was compiled from my own JS with the official hermes-compiler/hermes-engine npm packages, so they can be regenerated and committed as fixtures.

Alternatives considered

  • hermes-dec / hbctool: they work, but they're Python, version support lags (hermes-dec warns that v99 isn't formally supported) and they don't produce REA Evidence with offsets and identities.
  • Treating the bundle as an opaque file: that's the current state, and it hides most of what matters in a React Native app.

Possible direction

A Hermes reader in the artifact layer so inspect_artifact can classify the bundle and expose these facts (and APK/IPA inventories can point at it), following the same pattern as the PyInstaller reader. I'm happy to turn the prototype into a PR if this scope sounds right.

References

Originally created by @KJLavender on GitHub (Oct 24, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7038 ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal. - [x] I understand the contribution scope must be reviewed before integration; open questions are welcome here. - [x] This request does not include credentials, private samples, or sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Current limitation React Native apps usually ship their JavaScript as Hermes bytecode (`assets/index.android.bundle` in an APK, a `.jsbundle`/`.hbc` in iOS apps). Right now REA only recognizes React Native by native library names (`libhermes.so`, `hermes.framework`, see `docs/android-analysis.md`). The bundle itself is opaque: no version, no strings, no function names. #3608 covers this as part of a much bigger React reconstruction RFC. This is a proposal for a small first slice that doesn't depend on any of the rest: read the Hermes container itself, without disassembly or decompilation. ### Use cases The first questions on a React Native app are usually "which servers does it talk to", "what are the API paths", "is there a hardcoded key", and "roughly how is the code organized". Almost all of that is in the Hermes string table and function table, which can be read statically. As a check I ran a rough local reader on the public BlueWallet 8.0.2 APK (open source, Hermes bytecode v98, 14 MB bundle). The string table alone shows its Electrum servers (`electrum2.bluewallet.io:444`, `electrum.blockstream.info:60004`), the price APIs it calls, and 26,262 named functions out of 40,594, including the Metro runtime (`metroRequire`, `loadModuleImplementation`, ...). ### Desired outcome and success criteria For a Hermes bytecode file, either standalone or inside an APK/IPA inventory: 1. Header facts: bytecode version, source hash, declared file length, counts (functions, strings, identifiers, regexps, BigInts, CommonJS modules), option flags. 2. Integrity: whether the SHA-1 footer matches the bytes before it. 3. Function table: index, name (string id + text), parameter count, bytecode size and offset, kind (normal/generator/async/arrow), including the "overflowed" large headers. 4. String table: every string with its kind (string/identifier), encoding (Latin-1 or UTF-16) and file offset, including the overflow table. 5. Unsupported versions or delta-format files report a clear limitation instead of guessing. Non-goals for this slice: disassembly, decompilation, Metro module splitting, writing anything. ### Expected interaction with targets Read existing files or captured data ### Target and scope details (optional) Bytecode versions differ a lot (the small function header went from 16 to 12 bytes with a different bit layout around v97/v98, BigInt fields arrived in v87, etc.). I'd suggest starting with the versions current React Native ships (v96–v99) plus the older v83/v84 if it's cheap, and refusing others explicitly. ### Verification available so far I have a prototype or observed results ### Verification details (optional) Local prototype (~250 lines, Node only), compared field by field with hermes-dec 0.1.8 on Windows: | Input | Result | |---|---| | Same ES5 program compiled by hermesc 0.8.0, 0.11.0 and two 1.0.0 builds (bytecode v83, v84, v98, v99) | header, 39/39 strings, 6/6 functions identical | | Program with class/async/BigInt (v99) | identical, async and generator functions labelled | | Stress file: 802 functions, 3,204 strings, one 197 KB function, all four versions | identical; covers overflowed headers (all of them on v98/v99, the huge function on v83/v84) | | BlueWallet 8.0.2 `index.android.bundle` (v98, 14 MB) | 40,594/40,594 functions and 109,319/109,320 strings identical, read in about 0.16 s | The one difference on BlueWallet looks like a hermes-dec bug rather than mine: string #46390 is a single U+FEFF stored as UTF-16 (`ff fe`), and hermes-dec decodes it with Python's `utf-16` codec, which drops it as a BOM and returns an empty string. Every sample was compiled from my own JS with the official `hermes-compiler`/`hermes-engine` npm packages, so they can be regenerated and committed as fixtures. ### Alternatives considered - hermes-dec / hbctool: they work, but they're Python, version support lags (hermes-dec warns that v99 isn't formally supported) and they don't produce REA Evidence with offsets and identities. - Treating the bundle as an opaque file: that's the current state, and it hides most of what matters in a React Native app. ### Possible direction A Hermes reader in the artifact layer so `inspect_artifact` can classify the bundle and expose these facts (and APK/IPA inventories can point at it), following the same pattern as the PyInstaller reader. I'm happy to turn the prototype into a PR if this scope sounds right. ### References - #3608 (bigger React/Hermes RFC this would be a first slice of) - Hermes `BytecodeFileFormat.h` for the layouts - BlueWallet 8.0.2 release APK: https://github.com/BlueWallet/BlueWallet/releases/tag/v8.0.2
Author
Owner

@XinwangTao commented on GitHub (Oct 25, 2026):

这个切入点选得很好——把 #3608 里最独立的一块先落地,不依赖反汇编/反编译就能先覆盖取证时最常问的"连了哪些服务器、调了哪些 API、有没有硬编码 key"。你给的验证数据很有说服力(BlueWallet 上 40,594/40,594 函数、109,319/109,320 字符串与 hermes-dec 逐字段一致,0.16s 读完),BOM 那条差异也确实更像 hermes-dec 用 utf-16 codec 丢掉 U+FEFF 的问题,值得做成 fixture 用例固定下来。一个小问题:APK 里的 bundle 位于 assets/index.android.bundle,这个 slice 落地时是直接消费原始 bundle 文件,还是也需要处理 APK 内的定位/提取?另外建议在文档里明确版本矩阵(v96–v99 + v83/v84)和"不支持版本显式报错"的行为,避免误读。

<!-- gh-comment-id:6392670455 --> @XinwangTao commented on GitHub (Oct 25, 2026): 这个切入点选得很好——把 #3608 里最独立的一块先落地,不依赖反汇编/反编译就能先覆盖取证时最常问的"连了哪些服务器、调了哪些 API、有没有硬编码 key"。你给的验证数据很有说服力(BlueWallet 上 40,594/40,594 函数、109,319/109,320 字符串与 hermes-dec 逐字段一致,0.16s 读完),BOM 那条差异也确实更像 hermes-dec 用 utf-16 codec 丢掉 U+FEFF 的问题,值得做成 fixture 用例固定下来。一个小问题:APK 里的 bundle 位于 assets/index.android.bundle,这个 slice 落地时是直接消费原始 bundle 文件,还是也需要处理 APK 内的定位/提取?另外建议在文档里明确版本矩阵(v96–v99 + v83/v84)和"不支持版本显式报错"的行为,避免误读。
Author
Owner

@XinwangTao commented on GitHub (Oct 25, 2026):

Strong use case — Hermes bytecode is everywhere in modern React Native builds, and static reading of header/integrity/function/string tables would materially improve APK/iOS bundle triage without needing a JS engine. A few scoping thoughts: (1) start read-only (magic/version, source hash, function count, string table offsets) and keep any execution-related features out of the first slice; (2) the integrity field is worth surfacing even read-only since a mismatched hash immediately flags repackaging; (3) expose the bytecode version explicitly — Hermes versions drift across RN releases, and tooling needs to know which decoder applies.

<!-- gh-comment-id:6399873856 --> @XinwangTao commented on GitHub (Oct 25, 2026): Strong use case — Hermes bytecode is everywhere in modern React Native builds, and static reading of header/integrity/function/string tables would materially improve APK/iOS bundle triage without needing a JS engine. A few scoping thoughts: (1) start read-only (magic/version, source hash, function count, string table offsets) and keep any execution-related features out of the first slice; (2) the integrity field is worth surfacing even read-only since a mismatched hash immediately flags repackaging; (3) expose the bytecode version explicitly — Hermes versions drift across RN releases, and tooling needs to know which decoder applies.
Author
Owner

@KJLavender commented on GitHub (Oct 25, 2026):

Thanks. On the APK question: I'd put the reader in the artifact layer, keyed on the Hermes magic rather than the filename, so it handles both cases the same way: a standalone .hbc/.jsbundle, and the assets/index.android.bundle member that the APK inventory already lists. No separate extract step, the member just gets classified and its facts attached. I'd check the APK path explicitly in the PR rather than assume it.

Agree on documenting the version matrix and failing loudly outside it, that's the plan.

<!-- gh-comment-id:6402565119 --> @KJLavender commented on GitHub (Oct 25, 2026): Thanks. On the APK question: I'd put the reader in the artifact layer, keyed on the Hermes magic rather than the filename, so it handles both cases the same way: a standalone `.hbc`/`.jsbundle`, and the `assets/index.android.bundle` member that the APK inventory already lists. No separate extract step, the member just gets classified and its facts attached. I'd check the APK path explicitly in the PR rather than assume it. Agree on documenting the version matrix and failing loudly outside it, that's the plan.
Author
Owner

@jgt87 commented on GitHub (Oct 26, 2026):

Confirming on iOS, with a real-world v96 data point.

We hit this analyzing a shipping iOS app from the App Store: its React Native screens ship as Hermes bytecode v96 in .hbc resources packaged inside the IPA (each as .../<bundle>.zip → index.ios.hbc), 19 bundles, the largest ~23 MB. open_binary currently rejects them:

open_binary(index.ios.hbc) → target_unavailable: "unsupported binary format"

+1 on the slice as scoped — the header / string table / function table alone answer most of the first questions. Two small notes toward the version-range decision under "Target and scope details":

  • v96 is live in shipping iOS apps, not only v98/99 — worth including in the first slice.
  • The iOS .hbc-inside-IPA case (vs. Android index.android.bundle) is real here, so classifying .hbc resources during IPA inventory would cover it.

As an independent cross-check, we have a separate ~60-line Node v96 reader (string table incl. the overflow entries, small + large function headers, and the instruction stream driven by BytecodeList.def) that we used to build a UI-element + design-token atlas from these bundles — happy to share regenerable v96 fixtures if useful for that row.

<!-- gh-comment-id:6417424324 --> @jgt87 commented on GitHub (Oct 26, 2026): **Confirming on iOS, with a real-world v96 data point.** We hit this analyzing a shipping iOS app from the App Store: its React Native screens ship as **Hermes bytecode v96** in `.hbc` resources packaged inside the IPA (each as `.../<bundle>.zip → index.ios.hbc`), 19 bundles, the largest ~23 MB. `open_binary` currently rejects them: ``` open_binary(index.ios.hbc) → target_unavailable: "unsupported binary format" ``` +1 on the slice as scoped — the header / string table / function table alone answer most of the first questions. Two small notes toward the version-range decision under "Target and scope details": - **v96 is live in shipping iOS apps**, not only v98/99 — worth including in the first slice. - The **iOS `.hbc`-inside-IPA** case (vs. Android `index.android.bundle`) is real here, so classifying `.hbc` resources during IPA inventory would cover it. As an independent cross-check, we have a separate ~60-line Node v96 reader (string table incl. the overflow entries, small + large function headers, and the instruction stream driven by `BytecodeList.def`) that we used to build a UI-element + design-token atlas from these bundles — happy to share regenerable v96 fixtures if useful for that row.
Author
Owner

@XinwangTao commented on GitHub (Oct 27, 2026):

Good real-world confirmation for iOS v96 — that also exposes a packaging wrinkle worth handling in the same slice: the bundles arrive as .../.zip members (index.ios.hbc inside), so besides keying on the Hermes magic, the artifact-layer reader should do member-level classification inside zip/IPA containers, just like the APK inventory already lists assets/index.android.bundle. That keeps the "no separate extract step" approach consistent across both platforms. Adding v96 to the documented version matrix would also cover the shipping iOS default.

<!-- gh-comment-id:6431343668 --> @XinwangTao commented on GitHub (Oct 27, 2026): Good real-world confirmation for iOS v96 — that also exposes a packaging wrinkle worth handling in the same slice: the bundles arrive as .../<bundle>.zip members (index.ios.hbc inside), so besides keying on the Hermes magic, the artifact-layer reader should do member-level classification inside zip/IPA containers, just like the APK inventory already lists assets/index.android.bundle. That keeps the "no separate extract step" approach consistent across both platforms. Adding v96 to the documented version matrix would also cover the shipping iOS default.
Author
Owner

@macjayz commented on GitHub (Oct 27, 2026):

I would like to contribute to this feature, but I see @KJLavender already has the core parser prototype and intends to prepare a PR. I do not want to duplicate that implementation. If a maintainer would like a parallel, bounded slice, I can take the APK/IPA ZIP-member classification and integration tests (including an iOS v96 fixture) after agreeing the artifact-layer contract. Please advise whether that division is useful.

<!-- gh-comment-id:6441665184 --> @macjayz commented on GitHub (Oct 27, 2026): I would like to contribute to this feature, but I see @KJLavender already has the core parser prototype and intends to prepare a PR. I do not want to duplicate that implementation. If a maintainer would like a parallel, bounded slice, I can take the APK/IPA ZIP-member classification and integration tests (including an iOS v96 fixture) after agreeing the artifact-layer contract. Please advise whether that division is useful.
Author
Owner

@KJLavender commented on GitHub (Oct 27, 2026):

Thanks @jgt87, that's a useful data point. v96 wasn't in my test matrix, so I added it before replying.

The v96 compiler ships inside the react-native package: the hermesc bundled with RN 0.74 and 0.76 both report HBC bytecode version: 96. I compiled the same samples with the RN 0.76 hermesc and compared the reader against hermes-dec. Header fields, every string and every function entry (name, params, size, offset) match. That includes the stress file, which forces overflowed ("large") function headers. It's now in the CI run alongside v83/v84/v98/v99: https://github.com/KJLavender/format-readers/blob/main/hermes/test/run.mjs

One thing I noticed: the v96 compiler rejects class syntax outright, so RN apps on v96 have all of that lowered by Babel first. Not a reader issue, just why the class/async sample only runs on v98+.

Your v96 fixtures from a real app would still help, since compiler output from my samples is cleaner than a 23 MB production bundle. If you can share regenerable ones (or the source + RN version to rebuild them), I'll add them.

@macjayz the split sounds good to me: I take the reader (header, strings, function table, version matrix, failing loudly outside it), you take ZIP-member classification for APK/IPA, including the <bundle>.zip → index.ios.hbc nesting, plus the integration tests. The reader would expose a single "is this Hermes, and what's in it" entry point keyed on the magic, so the container side only has to hand it member bytes. Happy to adjust if a maintainer prefers a different cut. Waiting on the scope call before either of us opens a PR.

<!-- gh-comment-id:6447150710 --> @KJLavender commented on GitHub (Oct 27, 2026): Thanks @jgt87, that's a useful data point. v96 wasn't in my test matrix, so I added it before replying. The v96 compiler ships inside the `react-native` package: the hermesc bundled with RN 0.74 and 0.76 both report `HBC bytecode version: 96`. I compiled the same samples with the RN 0.76 hermesc and compared the reader against hermes-dec. Header fields, every string and every function entry (name, params, size, offset) match. That includes the stress file, which forces overflowed ("large") function headers. It's now in the CI run alongside v83/v84/v98/v99: https://github.com/KJLavender/format-readers/blob/main/hermes/test/run.mjs One thing I noticed: the v96 compiler rejects `class` syntax outright, so RN apps on v96 have all of that lowered by Babel first. Not a reader issue, just why the class/async sample only runs on v98+. Your v96 fixtures from a real app would still help, since compiler output from my samples is cleaner than a 23 MB production bundle. If you can share regenerable ones (or the source + RN version to rebuild them), I'll add them. @macjayz the split sounds good to me: I take the reader (header, strings, function table, version matrix, failing loudly outside it), you take ZIP-member classification for APK/IPA, including the `<bundle>.zip → index.ios.hbc` nesting, plus the integration tests. The reader would expose a single "is this Hermes, and what's in it" entry point keyed on the magic, so the container side only has to hand it member bytes. Happy to adjust if a maintainer prefers a different cut. Waiting on the scope call before either of us opens a PR.
Author
Owner

@macjayz commented on GitHub (Oct 27, 2026):

Thanks, @KJLavender. I confirm the split: I’ll handle artifact-layer ZIP-member classification and APK/IPA integration tests, including the nested <bundle>.zip → index.ios.hbc case and a regenerable iOS v96 fixture where available. I’ll keep the slice read-only and pass member bytes to the reader entry point keyed on Hermes magic; I won’t duplicate parser logic. Please let me know a convenient time for the scope call, and I’ll then start the focused implementation.

<!-- gh-comment-id:6448069895 --> @macjayz commented on GitHub (Oct 27, 2026): Thanks, @KJLavender. I confirm the split: I’ll handle artifact-layer ZIP-member classification and APK/IPA integration tests, including the nested `<bundle>.zip → index.ios.hbc` case and a regenerable iOS v96 fixture where available. I’ll keep the slice read-only and pass member bytes to the reader entry point keyed on Hermes magic; I won’t duplicate parser logic. Please let me know a convenient time for the scope call, and I’ll then start the focused implementation.
Author
Owner

@XinwangTao commented on GitHub (Oct 28, 2026):

Agree with the split — reader (KJLavender) plus ZIP-member classification and integration tests (macjayz) is a clean cut, and holding both PRs until the maintainer scope call avoids churn. One small thing to pin in the shared contract: the reader entry point should return an explicit unsupported-version/unsupported-format signal (consistent with the "fail loudly outside the matrix" plan) so the classifier can surface it instead of silently skipping — that closes the gap where a member is classified as Hermes but the reader refuses it.

<!-- gh-comment-id:6457958677 --> @XinwangTao commented on GitHub (Oct 28, 2026): Agree with the split — reader (KJLavender) plus ZIP-member classification and integration tests (macjayz) is a clean cut, and holding both PRs until the maintainer scope call avoids churn. One small thing to pin in the shared contract: the reader entry point should return an explicit unsupported-version/unsupported-format signal (consistent with the "fail loudly outside the matrix" plan) so the classifier can surface it instead of silently skipping — that closes the gap where a member is classified as Hermes but the reader refuses it.
Author
Owner

@macjayz commented on GitHub (Oct 28, 2026):

Small correction to my last comment: I misread "scope call" as a meeting. I understand it means the maintainer's scope decision, so I'm just waiting on that like @KJLavender, and won't open a PR before it.

Agree with @XinwangTao's point on the contract. For the container side, it would help if the reader's entry point returned three distinct outcomes:

  1. not Hermes (magic doesn't match), so the member is left unclassified as today
  2. Hermes, supported, with the parsed facts attached
  3. Hermes, unsupported (version outside the matrix, or delta format), with the bytecode version and reason

That way an APK/IPA member that is recognised as Hermes but refused by the reader is reported as a limitation in the inventory instead of being silently dropped. The integration tests would cover all three, including the nested <bundle>.zip → index.ios.hbc case.

@morluto, whenever you have a moment, could you confirm whether this split and scope work for you?

<!-- gh-comment-id:6464302977 --> @macjayz commented on GitHub (Oct 28, 2026): Small correction to my last comment: I misread "scope call" as a meeting. I understand it means the maintainer's scope decision, so I'm just waiting on that like @KJLavender, and won't open a PR before it. Agree with @XinwangTao's point on the contract. For the container side, it would help if the reader's entry point returned three distinct outcomes: 1. **not Hermes** (magic doesn't match), so the member is left unclassified as today 2. **Hermes, supported**, with the parsed facts attached 3. **Hermes, unsupported** (version outside the matrix, or delta format), with the bytecode version and reason That way an APK/IPA member that is recognised as Hermes but refused by the reader is reported as a limitation in the inventory instead of being silently dropped. The integration tests would cover all three, including the nested `<bundle>.zip → index.ios.hbc` case. @morluto, whenever you have a moment, could you confirm whether this split and scope work for you?
Author
Owner

@KJLavender commented on GitHub (Oct 28, 2026):

Agreed, those three outcomes work for me, with one addition from the reader side: a file can have the Hermes magic and a supported version and still be structurally broken (truncated, or a table that runs past the end). I'd report that as its own outcome rather than folding it into "unsupported", since "we don't read v100 yet" and "this file is damaged" mean different things to whoever reads the inventory:

  1. not Hermes: magic doesn't match, member stays unclassified
  2. Hermes, read: version plus parsed facts
  3. Hermes, unsupported: version outside the matrix, or delta format, with the version and reason
  4. Hermes, malformed: supported version but the structure doesn't hold up, with what failed and where

The SHA-1 footer is a separate thing: a footer mismatch doesn't stop the read, so it'd be a fact attached to outcome 2 (the prototype already reports it that way), not a refusal.

The container side could fold 3 and 4 into one limitation if that's simpler there. I just want the reader to keep them apart.

<!-- gh-comment-id:6467095709 --> @KJLavender commented on GitHub (Oct 28, 2026): Agreed, those three outcomes work for me, with one addition from the reader side: a file can have the Hermes magic and a supported version and still be structurally broken (truncated, or a table that runs past the end). I'd report that as its own outcome rather than folding it into "unsupported", since "we don't read v100 yet" and "this file is damaged" mean different things to whoever reads the inventory: 1. **not Hermes**: magic doesn't match, member stays unclassified 2. **Hermes, read**: version plus parsed facts 3. **Hermes, unsupported**: version outside the matrix, or delta format, with the version and reason 4. **Hermes, malformed**: supported version but the structure doesn't hold up, with what failed and where The SHA-1 footer is a separate thing: a footer mismatch doesn't stop the read, so it'd be a fact attached to outcome 2 (the prototype already reports it that way), not a refusal. The container side could fold 3 and 4 into one limitation if that's simpler there. I just want the reader to keep them apart.
Author
Owner

@XinwangTao commented on GitHub (Oct 30, 2026):

这个四分类方案很清晰,我赞同把 malformed 单独拆出来——"版本还不支持"和"文件本身损坏"对 inventory 阅读者确实是两个完全不同的信号,前者是能力边界、后者是数据质量问题,合并会让 triage 时误判。补充一点:malformed 结果里建议带上具体失败位置(比如哪个 table 越界、偏移量),这样后续排查损坏原因时不用重新解析;SHA-1 footer 作为 fact 附加在 read 结果上的处理我也同意,footer 缺失/不匹配只标注不拒读,符合只读 reader 的定位。等 maintainer 的 scope 决定后就可以按这个契约落地。

<!-- gh-comment-id:6515098095 --> @XinwangTao commented on GitHub (Oct 30, 2026): 这个四分类方案很清晰,我赞同把 malformed 单独拆出来——"版本还不支持"和"文件本身损坏"对 inventory 阅读者确实是两个完全不同的信号,前者是能力边界、后者是数据质量问题,合并会让 triage 时误判。补充一点:malformed 结果里建议带上具体失败位置(比如哪个 table 越界、偏移量),这样后续排查损坏原因时不用重新解析;SHA-1 footer 作为 fact 附加在 read 结果上的处理我也同意,footer 缺失/不匹配只标注不拒读,符合只读 reader 的定位。等 maintainer 的 scope 决定后就可以按这个契约落地。
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4258
No description provided.