Repository navigation
Conversation
Adds `audit_release_exposure` and `rea audit-release-exposure` to report source maps, Electron fuse implications, extractable payloads, debug information, build metadata, and credential markers. Checks carry stable IDs, observations, statuses, and fixed remediation, while incomplete and unassessed checks remain visible without implying a security verdict. Credential findings include only rule IDs and locations. The CLI supports `--fail_on` severity thresholds and exits 3 when an exposed check meets the threshold.
Codex Review Summary: 🔄 RunningThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f183381739
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
[Medium impact] The PR adds a bounded local-artifact audit through CLI and MCP entry points. The PR should not merge yet: earlier findings about unbounded shebang reads, lost map observations, and false Go path findings remain unresolved. FindingsFix with agent prompt### Issue 1
src/artifacts/ReleaseExposureReader.ts:1408-1409
On Linux, `café.map` and `cafe\u0301.map` can be separate files. If both are read as linker maps before a script declares only `café.map`, the new normalized bucket deletes both observations. The undeclared linker map disappears from `native.debug_companions`, and `--fail_on medium` can exit 0.
Keep distinct source occurrences separate when retracting a linker-map guess. The repository requires source identity and completed observations to survive lookup normalization.
### Issue 2
src/artifacts/ReleaseExposureReader.ts:1214-1219
Refunding `mapsRead` here removes the limit on full reads of shebang `.map` files. Each file has already received a read of up to 1 MiB, but those bytes do not count toward `textBytes`. Even after the text allowance is spent, later files still get that first read and refund its slot. A compressed .NET bundle can therefore force gigabytes of decompression instead of stopping full map reads after 32 files.
The repository requires resource limits before costly work. Detect the shebang from a bounded header, reuse bytes already read, and keep actual reads charged.
> **How this was verified:** Compressed members reach `readMap` on each iteration, and this branch restores its slot without charging the first read to `textBytes`.
### Issue 3
src/application/releaseExposure/ReleaseExposureService.ts:undefined-1105
GNU ld treats `-Map=-` as “write the map to standard output,” not as a filename. With `-ldflags` containing `-extldflags=-Wl,-Map=-`, the new `Map` alternative makes `goRelativeFlagPaths` return `"-"`. A build with no other paths then reports `go.build_paths` as `exposed` and wrongly exits 3 under `--fail_on low`.
Keep the option’s meaning through path extraction so this standard-output value is excluded without dropping real map filenames.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.SummaryAdds a local release-exposure audit through shared CLI and MCP workflows.
Diagram%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[CLI or MCP request] --> B[ReleaseExposureService]
B --> C[Inventory and bounded member reads]
C --> D[Source maps, companions, and text coverage]
C --> E[Credential marker scan]
B --> F[Desktop, fuse, Go, and Rust readers]
D --> G[Fixed check report]
E --> G
F --> G
G --> H[Evidence]
H --> I[Optional CLI severity gate]
Reviews (123) · Last reviewed commit: "fix(release-exposure): count unread text..." · Reviewed by Greptile |
…lags and cancellation Probe files with unrecognized names as text, keep undecided fuse slices unknown, forward opaque container coverage, read paths from Go build flags, and compare inventoried digests before accepting owner facts. Keep typed cleanup failures and attach the partial report when a later owner is cancelled. Pin the verifier's @electron/fuses version and move the tests to the named contract family.
…ing coverage gaps Reopen package members by their source spelling and keep caller path traversal in directory reads. Parse quoted and attached Go build flags, bind a native image fuse read to its own digest, and keep reader resources and the audit's partial report when cleanup or cancellation combine failures.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ffc2f2309e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 34811879c7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c7692515e0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ed map references Package members were matched to container entries by sourcePath, which is not unique in a PyInstaller executable, through a queue that only moved when a member was reopened. Bind by container-relative path and duplicate ordinal, the numbering inventory uses, so read order cannot change which bytes a member gets. A sourceMappingURL naming a file without a map extension was ignored. Local targets are now examined as maps whatever their name, and a target already scanned as plain text leaves coverage unknown.
URL schemes are case-insensitive, so an inline declaration such as //# sourceMappingURL=DATA:application/json;base64,... was routed to the local-reference path, which ignores scheme URLs. Its embedded sources were silently dropped and source_map_sources reported not_observed.
…ched ones A sourceMappingURL that decodes to a decomposed name (cafe%CC%81.json) never matched the inventoried member, whose logical path is NFC, so the map was scanned as ordinary text and its sourcesContent went uncounted. Map-target lookups now use an NFC key on both the record and lookup side; the declared URL is left as written. A declared map target that is never reached is now counted as a skipped map, so the source-map checks read as unknown coverage instead of not_observed.
…ilure An indexed map whose one leaf has malformed mappings (such as AA) threw a format failure that only recovered leaves for unsupported sections, so a valid sibling leaf with sourcesContent was dropped and javascript.source_map_sources read unknown instead of exposed. Leaves are now recovered for any format failure, with the envelope parse guarded so invalid JSON still falls through to the unparsed count. A nested package that inventory recognized but did not expand was probed as a plain binary and left the source-map checks at not_observed. It now marks coverage unknown and records a limitation. Reader tests assert the javascript.source_map_sources status for the NFC map-name cases as well.
c769251 to
f553459
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f553459c9a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…le member An unencrypted nested package that inventory recognized but did not expand is still a member its parent package already read. Count it as readable so packaging.extractable_payload reports exposed, while its own contents keep coverage unknown.
The fallback walk after a map format failure expanded every section and built a full ancestor path per leaf, so a map refused for nesting depth could still allocate far past the decoder budgets. Walk only to the 64-level section depth and the shared record budget, keeping leaf maps without materializing section paths.
A flag such as -I\\buildhost\sdk\include matched none of the path roots in GO_FLAG_PATH, so a network path that was the only one in complete build info left go.build_paths at not_observed. Accept \\host\share roots alongside the slash-rooted, dot-relative and drive-letter forms.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 169c978598
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The desktop owner names Electron only from an ASAR layout, so an asar: false build has no candidate. A complete scan with no candidate was reported as not_electron, which outranks unresolved and safe fuse outcomes and hid them as not_applicable. Leave the family unknown unless the fuse holder rules Electron out: an app bundle with no Electron Framework, or a directory with complete coverage whose native images were all read and hold no sentinel. A directory with no native image is not proof, so its fuse checks now report unknown.
…llation A scan cancelled after some members were examined reported its partial scan only when cleanup also failed, so a clean cancellation dropped the recovered source-map and credential observations. A cancelled scan now hands its assembled facts to the interrupted-scan visitor with unknown coverage and a cancellation limitation, and the caller still receives the cancellation error carrying that partial audit.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 580aec3faf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ber scan is cancelled Since the member scan keeps what it examined on ordinary cancellation, the status guide no longer says that cancelling the scan returns no partial report. It now describes the partial observation and limits the no-report case to cancellation before the scan can be assembled.
… short A text-named member read with one or two bytes of the text allowance left returns only `#` or `#!`, which cannot establish a Node shebang, so `runner.txt` kept its non-script role and its unexamined inline map was treated as absent. A partial or missing header that could still start a shebang line now leaves the member's maps unassessed, as an unread member's do once the allowance is spent.
…SAR layouts Inside an ASAR layout, a sibling whose name had a dot and no `.exe` suffix, such as `my.app`, was neither ranked for the native sample nor counted as a possible Electron host. With shared libraries filling the sample, that image could be dropped while the layout still counted its host as read, so safe fuses on a helper reported `run_as_node` as not observed. A dotted sibling that is not named like a shared library is now sampled after plain siblings and counted as a possible host.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0e729e7a3a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The env shebang reader split the whole line on spaces before handling -S, so a launcher such as `#!/usr/bin/env -S NODE_OPTIONS='--conditions development' node` named `development'` as its program and its maps were never assessed. An -S operand now takes the rest of the line and is split the way GNU env -S splits it: single and double quotes, backslash escapes, \_ and \c, and a leading # comment. A string env itself rejects, such as an unterminated quote, runs no program.
When a Go, Rust, fuse or desktop owner finished its Evidence and then saw cancellation, completeProviderEvidence returned that Evidence as the error's partial observation, but the audit only kept the error and substituted a not-run fact, so a recovered Go build path or Rust package list reported unknown. Each owner's Evidence-to-fact reading now lives in one function used for both a successful read and a completed partial, so the partial passes the same schema and inventory digest checks before its fact is kept. Later owners still do not run.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 41929a061b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…essed
`env -S` expands `${NAME}` from the environment, so a launcher such as
`#!/usr/bin/env -S ${NODE_BIN}` may run Node, but the shebang reader kept
the placeholder as the program name and treated the file as plain text,
reporting its inline map as absent. The env splitter now marks fields that
expand a variable, and a program whose name holds one leaves the shebang
undecided: the member's maps count as unassessed, and a `.map` companion
with such a header is not taken as a linker map. Quoted or escaped `${`
stays literal, and a resolved basename such as `${DIR}/bin/deno` still
decides.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c6a5afbf3b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A directly selected app.asar whose header exceeds asar.max_header_bytes now returns the typed header admission refusal and its recovery, as the directory scan already does, instead of a generic partial report.
…am unassessed
An undeclared `runner.map` whose `env -S` program is `${NODE_BIN}` may
run Node, so `examineMapMember` now treats an undecided `nodeShebang`
result as an unassessed map and companion, as `classifySiblingMap`
already does, rather than falling through to a linker-map guess.
splitForwardedLinkerFlags hands -Wl,-Map=private-link.map to the linker side, but LINKER_PATH_FLAG had no Map entry, so the relative map path was dropped and go.build_paths read not_observed. Add Map to the file-valued linker options so -Map=FILE and -Map FILE are both reported.
An oversized .map whose shebang names its program with an unresolved variable, or a package member with an undecided PE header, returned before counting its unscanned text, so credential markers could read not_observed. Count any non-bytes read as skipped text in both branches.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c5d9afcf6f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
# Conflicts: # src/composition/capabilityDeclarations.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6ac1dee17d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 15bc3db523
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c878c1f5b7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| : !droppedOutsideHost && | ||
| layouts.every( | ||
| (folder) => | ||
| settledLayouts.has(folder) || | ||
| hostRead(preferred.filter((image) => inLayout(folder, image.path))), |
There was a problem hiding this comment.
Keep partial inventory from certifying fuse-host coverage
When the 2 GiB inventory allowance stops a directory scan after a/a.exe but before a separate asar: false build under b/, complete desktop discovery still supplies only A's ASAR layout. Because droppedOutsideHost considers only inventoried images, this branch can set electronExecutableIncluded to true despite scan.coverage === "unknown", causing fuseCheck() to report A's safe fuses as not_observed for the whole selection. Fresh evidence beyond the earlier dropped-sample fix: a bounded check of the current sampling/reducer helpers returns not_observed when B is absent from the partial inventory, versus unknown when B's sentinel-less host is inventoried. Preserve incomplete-inventory uncertainty independently of whether every discovered layout has a sampled host.
AGENTS.md reference: AGENTS.md:L19-L19
Useful? React with 👍 / 👎.
A declaration recorded only its NFC lookup key, so every member or sibling sharing that key took the declared JavaScript-map route. On a normalization-sensitive filesystem an unrelated linker map spelled in another form lost its companion evidence when the declaring script was scanned first. Record the resolved spellings each declaration writes and treat only an exact match as declared. Another spelling in the bucket is read as an undeclared map; when no declared spelling is reached, its linker-map guess stays unresolved coverage rather than companion evidence.
Summary
REA shows what can be recovered from a shipped application, but a developer who wants that answer for their own release has to run many tools and combine the results by hand.
This adds
audit_release_exposureandrea audit-release-exposure. It reads one.app, install directory, ASAR or executable through existing readers and reports what static analysis recovers.--fail_on high,mediumorlowexits 3 on a matching exposed check, so it can gate a release in CI. It never runs or rewrites the artifact. It is an audit only, with no obfuscation or active protection. No issue is linked. This is a proposed new read-only tool.Problem and expected behavior
Use case: a team about to ship an Electron app, Go binary or packaged executable wants to know whether source maps, debug companions, build paths or credential-shaped strings went out with it, and whether Electron fuses are set.
Observable outcome:
exposed,not_observed,not_applicable,unknownornot_assessed, each with severity, locations and remediation.--fail_on, the exit status reflects only whether the audit finished.Change and scope
src/domain/releaseExposure.tsowns the report and allowances,src/artifacts/ReleaseExposureReader.tsowns the bounded scan, andsrc/application/releaseExposure/ReleaseExposureService.tscomposes existing owners. Electron fuses, Go, Rust, and artifact inventory are reused rather than reimplemented.A few changes reach existing owners:
inventoryArtifactgains optionalacquisitionLimits. Only this audit sets them, at 65,536 entries and 2 GiB of hashed bytes. Exhausting one keeps a partial inventory with unknown coverage. Other callers are unchanged.src/domain/binaryTarget.tsaddsinspectPeCodeViewFromBuffer, which separates absent CodeView records from incomplete parsing.parsePeCodeViewFromBufferkeeps its behavior on top of it.src/artifacts/inventory/classify.tsexportsclassifyEmbeddedExecutablePackage, so the audit recognizes a packaged executable with the same probes as inventory.The audit is written to fail toward
unknown: unresolved or oversized source maps stay undecided rather than becoming linker maps, incomplete Go metadata stays unknown, and a skipped PE stays unknown. Same-name package members are each scanned, and inlinedata:source maps in JavaScript are examined.Audits use
record-and-continuefor declared integrity, because signing an unpacked native module after packaging is routine. A real Electron app failed the audit outright before this.Further unknown-preserving behavior:
.env.production, is probed from its first 4 KiB and scanned as text when it has no NUL bytes. UTF-16 files without a recognized name are not detected.unknown, andexposedstill wins.go.build_pathsreads paths fromCGO_*FLAGS,-ldflags,-gcflagsand-asmflags. Quoted and attached flags are parsed, but a quoted field holding two space-separated paths with no flag between them reads as one path.unknown. A fuse read through an app bundle only checks membership in the inventoried digest set. This is race handling for a build replacing an executable mid-audit, so maintainers may prefer to drop it.Not included: obfuscation, stripping, signing, modifying the artifact, network calls, or running the target.
native.hardening,macho.hardened_runtimeandandroid.manifeststaynot_assessedand name their next probes.Follow the contribution scope.
Unresolved scope, authority, side effects, or required verification block
integration. Link the required maintainer scope decision for new providers or
capabilities involving target execution, injection, mutation, or protection
recovery; ordinary fixes within existing boundaries may state "not applicable".
Draft PRs may leave the scope decision pending and the readiness and merge checklists
unchecked. Maintainers can help resolve open questions.
Contract and boundary impact
text.credential_markersis a small vendor-prefix list insrc/domain/releaseExposureCredentialMarkers.ts, and REA may prefer not to own that list.audit_release_exposuretool andaudit-release-exposurecommand, plus exit status 3 for--fail_on. Catalog, README and localized README counts are updated..app, install directory, ASAR or executable on any host. Universal Mach-O slicing useslipoon macOS through the existing reader.docs/public/product-catalog.json), package, or installation impact (CI generates and validates ignored catalogs andskills/; edit authored instructions in.agents/skills/reverse-engineer-anything/and never commit build outputs): additive tool count and onepackage.jsonverifier script. No runtime or development dependency is added:verify:release-exposureinstalls@electron/fusesinto a temporary prefix when it runs, so it needs network access. No build output is committed.Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.
The process and file boxes describe the existing composed readers, not the audited target: they may start
lipoorxcrunand write private temporary files. The target itself is never launched or changed.Evidence and regression coverage
npm run verify:release-exposurebuilds an Electron fixture, audits it, applies the remediation with@electron/fuses, and audits again.docs/verification/release-exposure-2026-10-31.mdholds the record. Two installed Electron apps were audited by hand.rea audit-release-exposure ... --jsonon this commit. The demo below is a rendered walkthrough of these results, not a screen recording.unknown. That is the honest result, but maintainers may want different allowances.electron.asar_integritystayedunknownon the fixture.For boundary fixes, identify the applicable boundary regressions
and lifecycle regressions
covered by the evidence above, and explain relevant gaps.
For evidence-bearing changes:
Demo
Validation performed
Latest review follow-up: 264 release-reader source tests and check:fast passed; the filesystem Unicode-collision regression skipped on this Mac and remains unverified on Linux locally. Validation uses constructed artifacts; no real-target/provider verification was run for this follow-up.
Compiler path separator follow-up: two reproducing regressions failed before the fix; the release reader and service suites passed 482 tests and
npm run check:fastpassed after it. The 12 CLI/filesystem/MCP boundary tests below predate this final follow-up. No external Go binary was tested for this change.Latest review fixes preserve unknown coverage for encrypted packaged members, unread native images and ambiguous GNU
-Rruntime-token operands. The release-exposure source suites passed (504 tests),npm run check:fastpassed, and CLI, filesystem and MCP boundary tests passed (12 tests). These additions use constructed artifacts and synthetic Go metadata; no external PyInstaller package or Go binary was tested for them.Release-exposure review fixes:
npm run check:fastpassed and reader/service source suites passed (476 tests), including regressions for shebang-map read budgets, quoted Go flag fields, linker map stdout and GNU-Roperands. The reader reuses already-read script bytes and retains map charges. CLI, filesystem and MCP boundary tests passed (12 tests); those boundary tests ran before the final Go-flag parser change. Bun sourcemap coverage was already covered by existing reader tests.Run on this commit on macOS with Node 24.22.1 and npm 11.16.0:
npm run test:localon the changed source tests: 128 passed.npm run test:focusedon the changed acceptance and boundary tests: 182 passed.npm run verify:release-exposure: passed, with--fail_on highexiting 3 before remediation.typecheck,lint,lint:typed(0 errors),format:check,metadata:check,verify:architecture-guards,verify:test-discovery,knip:productionanddocs:check: passed.npm run checkstops atknipon one unused exported type atsrc/process/DarwinHelperCache.ts:465. This PR does not touch that file, and it failed the same way onmainin this environment.Compatibility, safety, and release
acquisitionLimitsoption are additive.package.jsonscript. No new runtime dependency.lipo. Matched credential bytes and source map text stay out of the report, and every read is bounded.Author readiness
Human owner: @mvanhorn
Before merge
type(scope): outcome.AI was used for assistance.