Declare asInvoker in the Windows binaries #117

Merged
kleb merged 1 commit from fix/windows-asinvoker-manifest into main 2026-08-26 02:01:29 +02:00
Collaborator

Summary

Removes the cause behind staging the updater under a different name, rather than continuing to avoid one keyword list.

Windows installer detection decides what an executable is by reading its filename: an unmanifested binary named anything like an installer is assumed to need elevation, so CreateProcess refuses to launch it from an ordinary process and fails with EACCES. Bun embeds a manifest but declares no execution level, which leaves that guess enabled. Declaring asInvoker ends the guessing for whatever name the binary is ever given.

Evidence

Spawning copies of one identical binary on Windows 11, all named probe-update-*.exe:

binary spawn
Bun's manifest, no trustInfo EACCES
same, plus asInvoker SPAWN OK

The manifested binary still runs normally, which matters because these are single-file executables with a payload appended past the resource section.

Implementation

mt.exe merges the trustInfo block into the compiler's own manifest rather than replacing it, so longPathAware and heapType survive a Bun upgrade untouched. Merging happens before the artifact is hashed, since the manifest is part of what the digest covers. Applied to the agent CLI's Windows release and to the Windows server binary.

resedit was tried first and cannot do this job: pe-library refuses a Bun executable outright with After Resource section, sections except for relocation are not supported. The dependency was backed out, so package.json and the lockfile are unchanged.

Where mt.exe is absent the build warns and continues rather than failing, because the staged name already avoids the heuristic on its own. PLANNER_MT_EXE overrides discovery.

Verification

Full typecheck, type-aware oxlint, knip, oxfmt pass. A real build:agent-cli run was inspected: the produced planner-windows-x64.exe carries requestedExecutionLevel level="asInvoker" uiAccess="false", still carries longPathAware, and runs.

No automated test: the behaviour needs a real PE and the Windows SDK, so there is no pure logic left to assert against once the work is a call to mt.exe. The staged-name regression test from #116 still guards the other half.

## Summary Removes the cause behind staging the updater under a different name, rather than continuing to avoid one keyword list. Windows installer detection decides what an executable is by reading its filename: an unmanifested binary named anything like an installer is assumed to need elevation, so `CreateProcess` refuses to launch it from an ordinary process and fails with `EACCES`. Bun embeds a manifest but declares no execution level, which leaves that guess enabled. Declaring `asInvoker` ends the guessing for whatever name the binary is ever given. ## Evidence Spawning copies of one identical binary on Windows 11, all named `probe-update-*.exe`: | binary | spawn | |---|---| | Bun's manifest, no `trustInfo` | EACCES | | same, plus `asInvoker` | SPAWN OK | The manifested binary still runs normally, which matters because these are single-file executables with a payload appended past the resource section. ## Implementation `mt.exe` merges the `trustInfo` block into the compiler's own manifest rather than replacing it, so `longPathAware` and `heapType` survive a Bun upgrade untouched. Merging happens before the artifact is hashed, since the manifest is part of what the digest covers. Applied to the agent CLI's Windows release and to the Windows server binary. resedit was tried first and cannot do this job: `pe-library` refuses a Bun executable outright with `After Resource section, sections except for relocation are not supported`. The dependency was backed out, so `package.json` and the lockfile are unchanged. Where `mt.exe` is absent the build warns and continues rather than failing, because the staged name already avoids the heuristic on its own. `PLANNER_MT_EXE` overrides discovery. ## Verification Full typecheck, type-aware oxlint, knip, oxfmt pass. A real `build:agent-cli` run was inspected: the produced `planner-windows-x64.exe` carries `requestedExecutionLevel level="asInvoker" uiAccess="false"`, still carries `longPathAware`, and runs. No automated test: the behaviour needs a real PE and the Windows SDK, so there is no pure logic left to assert against once the work is a call to `mt.exe`. The staged-name regression test from #116 still guards the other half.
Windows installer detection decides what an executable is by reading its
filename: an unmanifested binary named anything like an installer is
assumed to need elevation, so CreateProcess refuses to launch it from an
ordinary process and fails with EACCES. Bun embeds a manifest but
declares no execution level, which leaves that guess enabled.

That is the root cause behind staging the updater under a different
name. The rename avoids one keyword list; this removes the guessing
entirely, for whatever name the binary is given.

The trustInfo block is merged into the compiler's own manifest rather
than replacing it, so longPathAware and heapType survive a Bun upgrade
untouched. Merging happens before the artifact is hashed, because the
manifest is part of what the digest covers.

mt.exe does the work. resedit cannot: pe-library refuses a Bun
executable outright with "After Resource section, sections except for
relocation are not supported", because the compiler appends its payload
past the resource section. Where mt.exe is absent the build warns and
continues, since the staged name still avoids the heuristic on its own.

Verified on Windows 11 by spawning copies of one binary under the name
probe-update-*.exe: unmanifested fails with EACCES, manifested spawns,
and the manifested binary still runs.
Collaborator

kReview review

Verdict: no findings

No findings to address in the reviewed diff.

No concrete correctness, security, or release-path defect is established by the visible changes and repository evidence.

Reviewed by kReview at d1d2a8cbb8. This comment is conservative and based only on the PR diff, metadata, and supplied repository context.

Est. cost ~$0.28 (145.9k in / 1.1k out / gpt-5.6-sol).

<!-- codex-forgejo-review --> <!-- codex-forgejo-review-head:d1d2a8cbb822355eb1ae80806e0524bc580efeb6 --> ## kReview review **Verdict:** no findings No findings to address in the reviewed diff. No concrete correctness, security, or release-path defect is established by the visible changes and repository evidence. _Reviewed by kReview at `d1d2a8cbb8`. This comment is conservative and based only on the PR diff, metadata, and supplied repository context._ _Est. cost ~$0.28 (145.9k in / 1.1k out / gpt-5.6-sol)._ <!-- codex-forgejo-review-state:eyJoZWFkU2hhIjoiZDFkMmE4Y2JiODIyMzU1ZWIxYWU4MDgwNmUwNTI0YmM1ODBlZmViNiIsInN1bW1hcnkiOiJObyBjb25jcmV0ZSBjb3JyZWN0bmVzcywgc2VjdXJpdHksIG9yIHJlbGVhc2UtcGF0aCBkZWZlY3QgaXMgZXN0YWJsaXNoZWQgYnkgdGhlIHZpc2libGUgY2hhbmdlcyBhbmQgcmVwb3NpdG9yeSBldmlkZW5jZS4iLCJmaW5kaW5ncyI6W10sImN1bXVsYXRpdmVDb3N0Ijp7InVzZCI6MC4yNzUzMTEwMDAwMDAwMDAwMywiaW5wdXRUb2tlbnMiOjE0NTg5NSwib3V0cHV0VG9rZW5zIjoxMDY2LCJtb2RlbCI6ImdwdC01LjYtc29sIn19 -->
kleb merged commit d82ca9fcda into main 2026-08-26 02:01:29 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
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
knopersikcuo/Planner!117
No description provided.