Skip to content

feat(win32): default-terminal registration via ITerminalHandoff3 (#130) - #188

Merged
amanthanvi merged 13 commits into
mainfrom
issues/130-default-terminal-handoff
Sep 1, 2026
Merged

amanthanvi merged 13 commits into
mainfrom
issues/130-default-terminal-handoff

Conversation

@amanthanvi

@amanthanvi amanthanvi commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Summary

Makes noctty registrable as a Windows default terminal provider: a classic out-of-process COM server implementing ITerminalHandoff3, a MIDL proxy/stub so that interface can be marshaled into an unpackaged server, an adopted-PTY path that wires handed-off ConPTY handles into termio, and +register-default-terminal / +unregister-default-terminal with exact restore.

Landing as partial: registration, COM activation, cross-process marshaling and clean restore are all live-verified, but a handed-off session does not yet become a visible window. Details and the exact remaining defect are under Residuals.

Refs #130

Changes

  • src/apprt/win32_terminal_handoff.zig (new) — class factory + ITerminalHandoff3 object, -Embedding detection, pending-session queue, registry writers with snapshot/restore, opt-in failure trace. Reuses the COM idioms in win32_uia/com.zig, win32_toast_winrt.zig, win32_tab_drag_ole.zig and the registry style of win32_aumid.zig; no new COM framework.
  • src/apprt/win32_terminal_handoff_proxy/ (new) — Microsoft's ITerminalHandoff.idl plus MIDL-generated proxy/stub C checked in for x64 and arm64, so the build does not require the Windows SDK. Built as noctty-terminal-handoff-proxy.dll via src/build/TerminalHandoffProxy.zig.
  • src/cli/{register,unregister}_default_terminal.zig (new) + Action enum entries.
  • src/pty.zig — additive: WindowsPty control state becomes pseudo_console | handoff; adopted sessions resize by writing the private ConPTY signal packet to the supplied signal pipe.
  • src/termio/Exec.zig — additive adopted-session path: no spawn, no job object, no xev.Process, never terminates the adopted client; output EOF ends the session.
  • src/apprt/win32.zig, src/Surface.zig, src/config/Config.zig — small additive hooks for embedding mode and passing an adopted session through to Exec. No new user-facing config option; -Embedding is consumed as an argv switch.
  • Packaging — proxy DLL staged beside noctty.exe; Inno [Files]/[Registry] entries with uninsdeletekey on noctty-owned keys only. .github/workflows/release.yml deliberately untouched (see Residuals).
  • Docsdocs/windows.md "Default terminal" section, docs/status.md and capability-matrix rows.

Design notes worth reviewing

  • Windows' pipeline is two-stage: conhost activates DelegationConsole (IConsoleHandoff), and that OpenConsole process activates DelegationTerminal (ITerminalHandoff). noctty implements the terminal half only and refuses to register unless a compatible console half is already selected, since it cannot supply one.
  • Only ITerminalHandoff3 {6F23DA90-...} is implemented. It is the only terminal-handoff IID Windows Terminal 1.24 package-registers; each OpenConsole build calls exactly one compiled revision with no negotiation; and v3 is the revision that lets the terminal create its own pipes — which matters because v1/v2 hand over host-created pipes whose semantics do not match the overlapped named pipe noctty's libxev IOCP reader needs. v1/v2 are refused with E_NOINTERFACE plus one log line naming the minimum version.
  • The handoff supplies no HPCON, so resize writes the private ConPTY signal packet (opcode 8, u16 cols, u16 rows) to the signal pipe instead of calling ResizePseudoConsole. ConptyPackPseudoConsole is deliberately avoided.
  • Caller authorization uses CoImpersonateClient, not the marshaled server-process handle whose access mask we do not control.

Validation

Gates, all on this branch:

  • zig build -Demit-exe=true — exit 0
  • zig build test -Dtest-filter=handoff — exit 0
  • zig fmt --check over all 12 changed/new Zig files — exit 0
  • pwsh -NoProfile -File scripts/check-source-format.ps1 — exit 0, "PowerShell syntax and JSON validity checks passed."
  • pwsh -NoProfile -File test/windows/flagship/Test-VerificationContracts.ps1 — exit 0, "Windows x64 baseline checker probes: PASS", "flagship verification contracts: PASS (2 scenarios)"

Live, on Windows 11 26200 with Microsoft.WindowsTerminal 1.24.11911.0 installed and selected as default terminal.

Retained artifactsevidence/130/ in the shared evidence store, regenerated by the reproducible evidence/130/Capture-HandoffEvidence.ps1 (it records binary hashes, snapshots every touched registry value as raw bytes plus value kind with environment strings unexpanded, exercises COM, then restores and verifies):

  • registry-1-before.txt / registry-2-after-register.txt / registry-3-after-unregister.txt+register-default-terminal preserves DelegationConsole and flips DelegationTerminal from WT's {E12CFF52-...} to noctty's {33368C6F-...}, creating both CLSID keys and all three Interface\{IID}\ProxyStubClsid32 mappings. +unregister-default-terminal restores the prior value and removes them. File 3 is byte-for-byte identical to file 1 (the script asserts this and reports RESTORED EXACTLY).
  • transcript.txt — COM activation returns an object and starts one noctty.exe; cross-process QueryInterface returns hr=0x00000000 for ITerminalHandoff3 {6F23DA90-...} and hr=0x80004002 (E_NOINTERFACE) for both {59D55CCE-...} and {AA6B364F-...}, which is exactly the v3-only contract this PR describes.

Exercised in this session but no artifact retained (recorded here so a reader knows the difference):

  • Cross-process QueryInterface returns E_NOINTERFACE without our proxy registered, and WT's OpenConsoleProxy.dll fails to load into an unpackaged process with E_ACCESSDENIED — the reason we ship our own proxy. The S_OK half of this is captured above; the negative controls are not.
  • Launching cmd.exe drove the full chain: OpenConsole.exe -Embedding started, noctty started ~0.3 s later, and OpenConsole loaded noctty-terminal-handoff-proxy.dll within ~1 s.
  • Control test: with WT reselected, cmd.exe handed off into the running WT instance, confirming the trigger and the pipeline are valid.

The machine is restored to its exact original registry state after every capture run, and the restore is verified rather than assumed; no %%Startup, CLSID or Interface values are left behind.

Residuals / user steps

  1. A handed-off session does not yet become a visible window. With the authorization fix in place EstablishPtyHandoff no longer takes any refusal path (the failure trace stays empty), but two noctty processes briefly appear, no window shows, and cmd.exe exits on its own — i.e. the session is adopted and then torn down. Two things to chase: why a second process appears at all (the -Embedding branch may be falling through into normal startup / single-instance forwarding), and premature closing of the handed-off pipe or signal handles (likely the closeHandoffPipeCopies timing relative to COM marshaling, or the adopted Pty being copied rather than moved into the surface). Set NOCTTY_HANDOFF_TRACE=1 to get %LOCALAPPDATA%\noctty\handoff.log.
  2. No Settings picker entry. Candidate enumeration reads the com.microsoft.windows.console.host / com.microsoft.windows.terminal.host app-extension catalogs and accepts a pair only when both come from the same package, so an unpackaged noctty can be selected by registry but cannot appear in the Windows Settings or Windows Terminal picker. Selecting anything in the picker overwrites our pair. Package identity (sparse or full MSIX) would be required; not attempted here.
  3. Requires Windows Terminal installed for the console half. noctty implements no IConsoleHandoff.
  4. ARM64 proxy DLL unbuilt — generated sources are checked in and selected by the build, but this machine's Visual Studio lacks Microsoft.VisualStudio.Component.VC.Tools.ARM64.
  5. BLOCKER on feat(pty): own the ConPTY layer with a bundled OpenConsole + measured mangling catalog (#129) #164 — the proxy DLL ships without release-gate verification. noctty-terminal-handoff-proxy.dll is in both the portable zip and the installer, and it is loaded in-process by OpenConsole, but it is excluded from Authenticode re-verification (release.yml:337, verify-published-release.ps1:179) and from the Defender scan list (release.yml:388-392). package-windows.ps1 does sign it and the capability manifest covers it, so this branch's manifest/count invariants are intact (the $scanPaths.Count -ne 8 assertion still yields 8). Wiring it into both release gates must land with feat(pty): own the ConPTY layer with a bundled OpenConsole + measured mangling catalog (#129) #164 before a release ships this DLL; it is not a nice-to-have deferral.
  6. Inno installer not compiled locally (ISCC.exe unavailable); the static and flagship contracts pass.

Stacked on

Nothing — branched from main. src/pty.zig is additive and does not depend on #164's bundled-ConPTY resolver, but both touch that file, so expect a small merge.

Review R-188 dispositions

Fable review verdict was approve-with-changes; the GUID audit came back clean (all three ITerminalHandoff IIDs verified against upstream at the pinned commit, MIDL output authentic, no injected code). All blocking and medium items are addressed in this branch:

Item Disposition
H1 classifyConsoleHalf panic Fixed. GUID.parseNoBraces asserts dashes at 8/13/18/23 rather than erroring, so a 38-char braced dash-less value panicked (UB in ReleaseFast) instead of reaching the .invalid arm. Dash positions are now validated before the call. New negative test covers same-length/wrong-dash, misplaced-dash, trailing-dash and bad-hex shapes.
M2 dangling uninstall Fixed. [UninstallRun] runs +unregister-default-terminal (runhidden, RunOnceId) before file removal, so the uninstalling user's DelegationTerminal and proxy mappings no longer point at deleted files.
M3 compatibility check overclaims Fixed by softening, per the smaller of the two suggested options. The CLI message and docs/windows.md now say the check refuses a missing/empty/inbox console half, and the docs state plainly that a stale CLSID from an uninstalled Windows Terminal is accepted and will degrade console launches.
M4 LockServer write-only vs 5s idle timer Fixed. Server.isLocked() is now honored: the idle path refuses to exit while a client holds the class object, and it revokes the class object before deciding, then re-checks for a lock or a queued handoff and re-registers if anything landed in the gap. The last-window-close path goes through the same helper instead of calling PostQuitMessage directly.
M5 release gates skip the proxy DLL Recorded as a BLOCKER on #164 in Residuals above, not deferred as optional.
L6 same-user activation surface Documented. New "Security properties of registration" section in docs/windows.md describes the phishing shape, records that the per-user ProxyStubClsid32 gating means an unregistered machine is not exposed, and states that IL-mismatch rejection is correct by design so it is not later "fixed".
L7 STA ordering constraint Documented and guarded. The close site now checks currentApartmentIsSta() and refuses to close the marshaled copies outside an STA rather than silently corrupting the handoff; the constraint is also written into the docs section above.
L8 predictable pty pipe name Deferred as the review allows (pre-existing; same-user DoS, not hijack).
L9 setSize after shutdown Deferred as the review allows.

Trim list: applied 1-3, 5-7 and 9-10 (lock_count now honored rather than deleted; dead class_registered field removed; duplicate savePreviousInterfaceProxy call dropped; the operation-enum arrays and their near-tautological ordering test inlined into straight-line code with the intent kept as comments; the @typeInfo signature test dropped; the savePreviousTerminal wrapper inlined; the proxy README's build.zig attribution corrected to src/build/TerminalHandoffProxy.zig; Config.zig now uses error.SkipZigTest instead of silently passing on Linux CI). Item 4 applied: the [Files] Excludes: plus second Source: line collapsed to one recursive line. Item 8 resolved by deleting the unreferenced noctty-terminal-handoff-proxy.def so the two export lists cannot drift, with exports.c documented as the single source. The proxy README also now notes the uniform 2038 MIDL timestamp flagged in the audit.

Rebase

Rebased onto origin/main 5220df49e. The only conflicts were with #177's split of win32.zig into win32/consts.zig, win32/sys.zig and friends. Resolution kept main's structure: the constants block moved to consts.zig (WM_WINHOSTTY_TERMINAL_HANDOFF), the new CoGetApartmentType extern and APTTYPE* values moved to sys.zig, and this branch's call sites were rewritten to the sys./c. prefixes. Verified against a preserved pre-rebase branch: every other touched file is byte-identical, and the only lines lost from the win32.zig delta are exactly the nine declarations that were re-homed into consts.zig/sys.zig.

@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 53 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: 3db5cd21-3f4c-4243-a0ef-6fb29a37cc11

📥 Commits

Reviewing files that changed from the base of the PR and between 26f26ae and 01bbf48.

⛔ Files ignored due to path filters (1)
  • dist/windows/noctty.iss is excluded by !**/dist/**
📒 Files selected for processing (40)
  • .gitignore
  • build.zig
  • docs/status.md
  • docs/windows-capability-matrix.md
  • docs/windows.md
  • scripts/build-package-windows.ps1
  • scripts/package-windows.ps1
  • scripts/release-scan-defender.ps1
  • scripts/release-verify-artifacts.ps1
  • scripts/test-windows-build-capabilities.ps1
  • scripts/verify-published-release.ps1
  • src/Surface.zig
  • src/apprt/win32.zig
  • src/apprt/win32/consts.zig
  • src/apprt/win32/sys.zig
  • src/apprt/win32_terminal_handoff.zig
  • src/apprt/win32_terminal_handoff_proxy/ITerminalHandoff.idl
  • src/apprt/win32_terminal_handoff_proxy/LICENSE.microsoft-terminal
  • src/apprt/win32_terminal_handoff_proxy/README.md
  • src/apprt/win32_terminal_handoff_proxy/arm64/ITerminalHandoff.h
  • src/apprt/win32_terminal_handoff_proxy/arm64/ITerminalHandoff_i.c
  • src/apprt/win32_terminal_handoff_proxy/arm64/ITerminalHandoff_p.c
  • src/apprt/win32_terminal_handoff_proxy/arm64/dlldata.c
  • src/apprt/win32_terminal_handoff_proxy/exports.c
  • src/apprt/win32_terminal_handoff_proxy/x64/ITerminalHandoff.h
  • src/apprt/win32_terminal_handoff_proxy/x64/ITerminalHandoff_i.c
  • src/apprt/win32_terminal_handoff_proxy/x64/ITerminalHandoff_p.c
  • src/apprt/win32_terminal_handoff_proxy/x64/dlldata.c
  • src/build/TerminalHandoffProxy.zig
  • src/cli/ghostty.zig
  • src/cli/ghostty_action.zig
  • src/cli/register_default_terminal.zig
  • src/cli/unregister_default_terminal.zig
  • src/config/Config.zig
  • src/pty.zig
  • src/termio/Exec.zig
  • src/termio/Termio.zig
  • src/termio/backend.zig
  • test/windows/flagship/contracts/Contracts.80-Release.ps1
  • test/windows/flagship/contracts/Contracts.81-ReleaseSigning.ps1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 29, 2026

Copy link
Copy Markdown

Greptile Summary

This change adds Windows default-terminal registration, COM terminal handoff support, adopted-session plumbing, and required Windows packaging updates. The handoff path keeps pending sessions in process-owned state and uses random, collision-checked capabilities, so forged or replayed handoff messages cannot dereference arbitrary memory or consume a live session.

Confidence Score: 5/5

No blocking failure remains.

No accepted blocking findings remain after validating that unknown, replayed, and colliding handoff capabilities do not take or free pending sessions.

T-Rex T-Rex Logs

What T-Rex did

  • Ran the current-checkout handoff capability validation harness and compared it to the prior implementation.
  • Confirmed 17 passing checks in the validation, covering forged zero, unissued, maximum, replayed, and colliding capabilities, and verified that the genuine queued session remains available until its issued capability is consumed.
  • Tried to run the Zig-based handoff test, but Zig was not installed in this environment, so native execution could not start.
  • Validated that the message handler resolves process-owned capabilities before cleanup and that random capability generation prevents predictable-ID consumption.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (17): Last reviewed commit: "win32: randomize pending handoff capabil..." | Re-trigger Greptile

Comment thread src/apprt/win32.zig Outdated
@amanthanvi
amanthanvi force-pushed the issues/130-default-terminal-handoff branch from 7fdc981 to 4f1d031 Compare August 29, 2026 08:54
Comment thread src/apprt/win32.zig
@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai review
@codex review

Context for reviewers:

Registers noctty as a Windows default-terminal provider: a classic out-of-process COM server implementing ITerminalHandoff3, checked-in MIDL proxy/stub C for x64 and arm64 (so the build does not require the Windows SDK), an adopted-PTY path into termio, and +register-default-terminal / +unregister-default-terminal with exact restore.

Prior adversarial review audited every GUID/IID on the branch against upstream microsoft/terminal and found them clean, and confirmed the registry restore is byte-for-byte exact (before / after-register / after-unregister snapshots capturing value kinds with environment strings unexpanded). The PR is deliberately v3-only: v1/v2 are refused with E_NOINTERFACE, because those revisions hand over host-created pipes whose semantics do not match the overlapped named pipe our libxev IOCP reader requires.

Please scrutinise most:

  1. Caller authorization — we use CoImpersonateClient rather than the marshaled server-process handle whose access mask we do not control. Is the impersonation scope correct, and is it revert-safe on every error path?
  2. Adopted-session lifetime in src/termio/Exec.zig — no spawn, no job object, no xev.Process, and we must never terminate the adopted client; output EOF ends the session. Look for handle leaks or double-close on the supplied ConPTY / signal handles.
  3. The private ConPTY resize packet (opcode 8, u16 cols, u16 rows) written to the signal pipe in place of ResizePseudoConsole — byte layout, partial-write handling, and behaviour when the peer has gone.
  4. Registration refusal logic — we refuse to register unless a compatible DelegationConsole half is already selected. Confirm that check cannot be raced or bypassed into a half-registered state that leaves the user without a working terminal.

@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown

Rate Limit Exceeded

@amanthanvi have exceeded the limit for the number of chat messages per hour. Please wait 50 minutes and 45 seconds before sending another message.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 30, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-01T03:09:09.783955Z 01bbf48 Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f1d031197

ℹ️ 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".

Comment thread src/apprt/win32_terminal_handoff.zig
Comment thread src/apprt/win32_terminal_handoff.zig Outdated
Comment thread src/apprt/win32_terminal_handoff.zig
@amanthanvi

Copy link
Copy Markdown
Owner Author

@codex review

Round 2, pushed as d1a7de2. (Holding off on CodeRabbit — my earlier batch exhausted its hourly chat budget and every further mention tops it back up.) All four round-1 findings were real.

Untrusted handoff message pointer (greptile) — fixed, and worse than reported. Delivery is PostThreadMessageW, so the target is an enumerable thread id and UIPI lets any same-desktop, same-integrity process post it — and drainQueuedTerminalHandoffs had the same shape via PeekMessageW. Pending sessions now live in a mutex-guarded, server-owned table addressed by an issued PendingId; unknown or replayed ids are dropped, and a failed post hands ownership back.

CoRevertToSelf failure (codex) — fixed; I chose to crash. The impersonating thread is the process message loop, so continuing meant creating windows, reading config and spawning shells under the caller's token. The code now confirms the token is really attached, retries RevertToSelf, confirms again, and panics if it survives. Losing adopted windows plus one launch is recoverable — Windows falls back to a console window — whereas silently acting under a foreign identity is not. Quarantining the thread was not available.

LockServer-only idle check (codex) — fixed. LockServer is optional in the COM contract, so a client holding a live ITerminalHandoff3 was invisible to isLocked(). There is now a live_objects count incremented at creation and decremented on final Release, folded into a single isBusy() used by both idle checks.

Console-half TOCTOU (codex) — recheck added, rollback declined. The commit now re-reads DelegationConsole and refuses if it changed. I declined the transactional rollback deliberately: refusing at the commit needs none, because nothing about the selection has been written yet, and what remains is the same mid-sequence failure state the function already documents and that unregisterDefaultTerminal already restores.

Please concentrate on:

  1. The new pending-session table. Id issuance and reuse (can an id be replayed after the session is taken?), the lock discipline against the COM STA thread, and ownership on every failure path — a post that fails must not leak the session or free it twice.
  2. The panic decision. If you think crashing the embedding server is the wrong call, argue it — but the alternative has to actually clear the impersonation, not merely fail the current handoff.
  3. live_objects accounting. The decrement reads self.server into a local before releasing; check for a use-after-free of the server through the final Release, and for an interleaving where the count drops to zero between the busy check and the class revocation.

Merge-order note, handled centrally: #188 merges before #164, because #164's release gates require the proxy DLL this PR packages.

Out of scope but real, flagged rather than fixed: WM_WINHOSTTY_UPDATE and WM_WINHOSTTY_TOAST_ACTIVATION carry pointers in lParam in exactly the same way, but both live on main and are outside this PR's diff. Please do not re-report them here; they need their own change.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: d1a7de224f

ℹ️ 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".

@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai review

CodeRabbit specifically: earlier requests on this PR were dropped for rate limiting, so this is your first look. Now at d1a7de22.

This PR registers noctty as a Windows default-terminal provider: an out-of-process COM server implementing ITerminalHandoff3, checked-in MIDL proxy/stub for x64 and arm64, an adopted-PTY path into termio, and register/unregister with exact registry restore.

Highest-value areas, all from fixes landed this round:

  1. The pending-session table. WM_APP+7 previously carried a raw PendingSession pointer in lParam; since delivery is PostThreadMessageW, any same-desktop same-integrity process could post a forged pointer and crash the process. Sessions now live in a mutex-guarded server-owned table addressed by an issued PendingId. Check id issuance and reuse (can an id be replayed after the session is taken?), the lock discipline against the COM STA thread, and ownership on every failure path — a failed post must not leak the session or free it twice.
  2. live_objects accounting. LockServer is optional in the COM contract, so a client holding a live ITerminalHandoff3 was invisible to the idle check. There is now a live-object count folded into isBusy(). Look for a use-after-free of the server through the final Release, and for an interleaving where the count hits zero between the busy check and class revocation.
  3. The CoRevertToSelf failure path, which now panics. The impersonating thread is the process message loop, so continuing meant creating windows and spawning shells under the caller's token. If you think crashing is wrong, argue it — but the alternative has to actually clear the impersonation, not just fail the current handoff.
  4. Adopted-session lifetime in src/termio/Exec.zig: no spawn, no job object, never terminate the adopted client, output EOF ends the session. Look for handle leaks or double-close on the supplied ConPTY and signal handles.

Out of scope, please don't report here: WM_WINHOSTTY_UPDATE and WM_WINHOSTTY_TOAST_ACTIVATION carry lParam pointers the same way, but both live on main outside this diff and need their own change.

@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- changed files ---'
git diff --name-status origin/main...HEAD

echo '--- handoff outline ---'
ast-grep outline src/apprt/win32_terminal_handoff.zig --items all --view expanded || true

echo '--- Exec outline ---'
ast-grep outline src/termio/Exec.zig --items all --view expanded || true

echo '--- focused symbols and message references ---'
rg -n -C 4 \
  -e '\bPending(Id|Session)\b' \
  -e '\b(drainQueuedTerminalHandoffs|EstablishPtyHandoff|CoImpersonateClient|CoRevertToSelf|live_objects|isBusy|LockServer)\b' \
  -e '\bWM_WINHOSTTY_TERMINAL_HANDOFF\b' \
  src/apprt/win32_terminal_handoff.zig src/apprt/win32.zig src/apprt/win32 2>/dev/null || true

echo '--- adopted PTY and Exec references ---'
rg -n -C 4 \
  -e '\b(handoff|adopted|pseudo_console|WindowsPty|signal_pipe|closeHandoffPipeCopies)\b' \
  src/pty.zig src/termio/Exec.zig src/apprt/win32.zig src/Surface.zig 2>/dev/null || true

Length of output: 56169


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- pending queue, Server, and COM object lifetime ---'
sed -n '224,680p' src/apprt/win32_terminal_handoff.zig

echo '--- impersonation and recovery implementation ---'
sed -n '680,790p' src/apprt/win32_terminal_handoff.zig

echo '--- handoff message dispatch and idle revoke path ---'
sed -n '1828,1895p' src/apprt/win32.zig
sed -n '3180,3232p' src/apprt/win32.zig
sed -n '4400,4475p' src/apprt/win32.zig
sed -n '6720,6755p' src/apprt/win32.zig

echo '--- Windows PTY handoff ownership and resize ---'
sed -n '332,615p' src/pty.zig

echo '--- Exec adopted-session construction, stop, IO loop, and teardown ---'
rg -n -C 6 \
  -e '\b(adopted_client_process|AdoptedSession|handoffHandles|deinit\s*\(|pub fn create|pub fn init)\b' \
  src/termio/Exec.zig
sed -n '1040,1250p' src/termio/Exec.zig
sed -n '1420,1600p' src/termio/Exec.zig

echo '--- focused tests ---'
sed -n '1460,1600p' src/apprt/win32_terminal_handoff.zig
sed -n '1590,1685p' src/termio/Exec.zig

Length of output: 81387


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- every adopted-session transfer and cleanup site ---'
rg -n -C 8 \
  -e '\btakeAdopted\s*\(' \
  -e '\badopted_session\b' \
  -e '\bPendingSession\b' \
  src/Surface.zig src/apprt/win32.zig src/termio/Exec.zig src/apprt/win32_terminal_handoff.zig

echo '--- App setup, App teardown, and server drain/revoke calls ---'
rg -n -C 10 \
  -e '\b(terminal_handoff_server|drainPending|\.revoke\(\)|embedding_mode|CoInitializeEx)\b' \
  src/apprt/win32.zig

echo '--- source at transfer candidates ---'
sed -n '1,260p' src/Surface.zig
sed -n '4475,4610p' src/apprt/win32.zig
sed -n '1680,1805p' src/apprt/win32.zig

echo '--- complete diff for reviewed Zig implementation ---'
git diff --find-renames --unified=40 origin/main...HEAD -- \
  src/apprt/win32_terminal_handoff.zig \
  src/apprt/win32.zig \
  src/Surface.zig \
  src/pty.zig \
  src/termio/Exec.zig

Length of output: 105361


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- current commit ---'
git rev-parse HEAD

echo '--- exact ownership-transfer references ---'
rg -n -C 12 '\btakeAdopted\s*\(' src
rg -n -C 12 '\badopted_session\b' src/Surface.zig src/apprt/win32.zig src/termio/Exec.zig

echo '--- App initialization and teardown that own terminal_handoff_server ---'
rg -n -C 16 '\b(drainPending|terminal_handoff_server|terminal_handoff_server\.|deinit\(self: \*App|pub fn deinit)\b' src/apprt/win32.zig

echo '--- Surface initialization around adopted session ---'
ast-grep outline src/Surface.zig --items all --view expanded || true
rg -n -C 20 '\b(init|create|adopted_session|Exec\.init)\b' src/Surface.zig

Length of output: 96192


@amanthanvi I completed a static review of d1a7de224f81f3f53af2ca59422ab5174d5011d1.

I found no actionable issues in the requested areas.

The pending-session table has single-consumption ownership. take removes an entry while it holds the mutex. A replay cannot return the same PendingSession twice. The failed-post path removes the queued entry before it returns ownership to EstablishPtyHandoff.

The adopted session moves safely through PendingSession, the runtime Surface, and termio.Exec. Each transfer clears the prior owner. The adopted shutdown path closes the signal pipe and releases local handles. It does not terminate the adopted client or OpenConsole.

The impersonation path calls CoRevertToSelf after all successful impersonation paths. If that call fails, it checks the thread token, attempts RevertToSelf, and checks again. The panic is appropriate when the token remains attached because the UI thread must not continue under the COM caller identity.

The live_objects decrement uses the saved Server pointer after TerminalHandoff destruction. It does not access the destroyed object. The revoke-and-recheck sequence also covers an activation that becomes visible between the initial busy check and class-object revocation.

I did not run Windows execution tests in this environment.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@codex review

Final pre-merge review pass. This PR has already been through adversarial review and at least one bot round; findings were triaged as fix / decline-with-evidence / follow-up rather than applied blindly, so some flagged items are deliberate design with reasoning recorded in-thread. Please focus on anything that would be wrong at merge time: stale claims in the description versus the current head, cross-PR interactions, and correctness of security-relevant paths. If you flag something previously declined, please engage with the recorded reasoning rather than restating the original finding.

@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- PR metadata ---'
gh pr view 188 --repo amanthanvi/noctty --json number,title,headRefOid,baseRefOid,body,url,state

echo '--- current commit and diff summary ---'
git rev-parse HEAD
git diff --name-status origin/main...HEAD
git diff --stat origin/main...HEAD

echo '--- relevant source outlines ---'
for file in \
  src/apprt/win32_terminal_handoff.zig \
  src/pty.zig \
  src/termio/Exec.zig \
  src/apprt/win32.zig \
  src/Surface.zig \
  src/config/Config.zig \
  src/build/TerminalHandoffProxy.zig \
  scripts/package-windows.ps1 \
  scripts/verify-published-release.ps1 \
  .github/workflows/release.yml
do
  if [ -f "$file" ]; then
    echo "--- outline: $file ---"
    ast-grep outline "$file" --items all --view expanded || true
  fi
done

echo '--- merge/base relationship ---'
git merge-base origin/main HEAD
git rev-list --left-right --count origin/main...HEAD

Length of output: 18832


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- handoff implementation: declarations and sensitive symbols ---'
rg -n -C 10 \
  -e '^(pub )?(const|fn) .*?(Pending(Id|Session)|Server|TerminalHandoff|ClassFactory|EstablishPtyHandoff|registerDefaultTerminal|unregisterDefaultTerminal|classifyConsoleHalf|currentApartmentIsSta)' \
  -e '\b(pending|next_pending|live_objects|lock_count|isBusy|take|insert|remove|CoImpersonateClient|CoRevertToSelf|RevertToSelf|OpenThreadToken|CoRegisterClassObject|CoRevokeClassObject)\b' \
  src/apprt/win32_terminal_handoff.zig

echo '--- handoff implementation: complete numbered source ---'
nl -ba src/apprt/win32_terminal_handoff.zig | sed -n '1,1720p'

echo '--- application integration and message dispatch ---'
rg -n -C 12 \
  -e '\b(terminal_handoff|WM_WINHOSTTY_TERMINAL_HANDOFF|drainPending|embedding_mode|maybeExit|revoke|register)\b' \
  src/apprt/win32.zig src/apprt/win32/consts.zig src/apprt/win32/sys.zig src/Surface.zig src/config/Config.zig

echo '--- adopted PTY and Exec ownership / cleanup ---'
rg -n -C 12 \
  -e '\b(handoff|Adopted|adopted|pseudo_console|signal_pipe|closeHandoffPipeCopies|setSize|deinit|init|destroy|terminate|kill)\b' \
  src/pty.zig src/termio/Exec.zig src/Surface.zig src/apprt/win32.zig

echo '--- registration, installer, package build, and capability-contract diffs ---'
git diff --unified=30 origin/main...HEAD -- \
  dist/windows/noctty.iss \
  scripts/build-package-windows.ps1 \
  scripts/package-windows.ps1 \
  scripts/test-windows-build-capabilities.ps1 \
  build.zig \
  src/build/TerminalHandoffProxy.zig \
  src/cli/ghostty.zig \
  src/cli/ghostty_action.zig \
  src/cli/register_default_terminal.zig \
  src/cli/unregister_default_terminal.zig

echo '--- PR `#164` state and release-gate changes ---'
gh pr view 164 --repo amanthanvi/noctty --json number,state,mergedAt,headRefOid,baseRefOid,title,url,body
gh pr diff 164 --repo amanthanvi/noctty --name-only
gh pr diff 164 --repo amanthanvi/noctty -- \
  .github/workflows/release.yml \
  scripts/verify-published-release.ps1 \
  scripts/package-windows.ps1 2>&1 || true

Length of output: 33910


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- handoff: COM object, authorization, queue transfer ---'
sed -n '400,680p' src/apprt/win32_terminal_handoff.zig
sed -n '735,910p' src/apprt/win32_terminal_handoff.zig

echo '--- handoff: registry reads/writes and commit/restore ordering ---'
sed -n '995,1225p' src/apprt/win32_terminal_handoff.zig
sed -n '1570,1695p' src/apprt/win32_terminal_handoff.zig

echo '--- app: handoff queue callback, message handling, shutdown/revoke ---'
rg -n -C 20 \
  -e 'fn queueTerminalHandoff|queuePending|queue_session|takePending|drainPending|WM_WINHOSTTY_TERMINAL_HANDOFF|maybeExit|isBusy|\.revoke\(\)' \
  src/apprt/win32.zig
sed -n '4400,4640p' src/apprt/win32.zig

echo '--- PTY: adopted handle ownership, resize, and cleanup ---'
sed -n '300,650p' src/pty.zig

echo '--- Exec: adopted-session initialization, I/O termination, and cleanup ---'
rg -n -C 16 \
  -e 'AdoptedSession|adopted_session|handoff|pub fn init|pub fn deinit|fn deinit|fn stop|fn kill|CloseHandle|close' \
  src/termio/Exec.zig
sed -n '1020,1265p' src/termio/Exec.zig
sed -n '1400,1635p' src/termio/Exec.zig

echo '--- surface transfer ---'
rg -n -C 16 -e 'adopted_session|takeAdopted|Exec\.init|Exec\.create' src/Surface.zig src/apprt/win32.zig

echo '--- installer and package integration ---'
sed -n '1,240p' dist/windows/noctty.iss
git diff --unified=25 origin/main...HEAD -- \
  build.zig \
  scripts/build-package-windows.ps1 \
  scripts/package-windows.ps1 \
  scripts/test-windows-build-capabilities.ps1 \
  src/build/TerminalHandoffProxy.zig \
  src/cli/ghostty.zig \
  src/cli/ghostty_action.zig \
  src/cli/register_default_terminal.zig \
  src/cli/unregister_default_terminal.zig

echo '--- PR `#164` state and changed paths ---'
gh pr view 164 --repo amanthanvi/noctty --json number,state,mergedAt,headRefOid,baseRefOid,title,url
gh pr diff 164 --repo amanthanvi/noctty --name-only

Length of output: 167857


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- pending-ID generation and all uses ---'
rg -n -C 8 \
  -e '\b(next_id|PendingId|queuePending|takePending|PostThreadMessageW|WM_WINHOSTTY_TERMINAL_HANDOFF)\b' \
  src/apprt/win32_terminal_handoff.zig src/apprt/win32.zig

echo '--- entropy / random identifier sources in handoff implementation ---'
rg -n -i -C 3 \
  -e '\b(random|rand|bcrypt|rtlgenrandom|uuid|guid|nonce|cookie)\b' \
  src/apprt/win32_terminal_handoff.zig

echo '--- PR `#164` release gate patch lines ---'
gh pr diff 164 --repo amanthanvi/noctty | rg -n -C 10 \
  -e 'noctty-terminal-handoff-proxy\.dll' \
  -e 'verify-published-release' \
  -e 'release-scan-defender' \
  -e 'scanPaths' \
  -e 'Authenticode' \
  -e 'Get-AuthenticodeSignature' || true

echo '--- compare PR `#164` base to head for the affected release files ---'
gh api repos/amanthanvi/noctty/pulls/164/files --paginate \
  --jq '.[] | select(.filename == ".github/workflows/release.yml" or .filename == "scripts/verify-published-release.ps1" or .filename == "scripts/release-scan-defender.ps1") | {filename,status,patch}'

Length of output: 57988


@amanthanvi I completed a static final review of d1a7de224f81f3f53af2ca59422ab5174d5011d1.

I found no merge-blocking issue.

The pending-session table has single-consumption ownership. takePending removes an entry while it holds the mutex. A replay cannot obtain the same PendingSession twice.

The sequential PendingId is not an authorization secret. A same-integrity process can post a matching ID. However, the UI handler runs on the same STA after the COM method returns and the proxy marshals the returned handles. A forged message can only replace the legitimate wake-up. It cannot consume the session during EstablishPtyHandoff. If PostThreadMessageW fails, queueTerminalHandoff removes the entry before the method returns. Any queued forged message then resolves to an unknown ID.

The live_objects decrement is safe. TerminalHandoff.Release saves the Server pointer before it destroys TerminalHandoff. The idle path revokes the class object, then rechecks isBusy() before it exits.

The adopted-session transfer clears each prior owner. The shutdown path closes the signal pipe and local handles. It does not terminate the adopted client process.

The description’s #164 release-gate dependency remains current. #164 adds noctty-terminal-handoff-proxy.dll to Get-WindowsSignedRuntimePayloads. The shared list feeds the Authenticode verification and Defender scan paths. The stated merge order, #188 before #164, is required.

I did not run Windows runtime or release workflow validation in this environment.

You are interacting with an AI system.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d1a7de224f

ℹ️ 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".

Comment thread docs/windows.md Outdated
@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai full review
@greptileai review this draft
@codex review

@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

@amanthanvi I will perform a full review of the current PR state.

⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 59 minutes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d1a7de224f

ℹ️ 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".

Comment thread docs/windows.md Outdated
Comment thread src/apprt/win32_terminal_handoff.zig
Comment thread src/termio/Exec.zig
Comment thread src/apprt/win32_terminal_handoff.zig Outdated
amanthanvi added a commit that referenced this pull request Aug 31, 2026
#130
#188

Keep the embedding server alive while clients hold factory references, roll back COM registration writes when the selection commit fails, and propagate an adopted PTY's initial resize failure through surface initialization.

Mark the known nonfunctional live handoff as experimental and describe proxy registration as configuration rather than an authorization boundary.
@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@amanthanvi

Copy link
Copy Markdown
Owner Author

@greptileai

@amanthanvi

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Rate Limit Exceeded

@amanthanvi have exceeded the limit for the number of chat messages per hour. Please wait 2 minutes and 20 seconds before sending another message.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dcf9456c1c

ℹ️ 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".

Comment thread src/termio/Termio.zig Outdated
Comment thread scripts/package-windows.ps1
amanthanvi added a commit that referenced this pull request Sep 1, 2026
#130
#188

Keep backend cleanup with the caller until Termio initialization succeeds, avoiding a double free when an adopted session's initial resize fails.

Add the handoff proxy DLL to post-package Authenticode verification and Defender scanning, and update the fail-closed release contracts.
@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@amanthanvi

Copy link
Copy Markdown
Owner Author

@greptileai

@amanthanvi

Copy link
Copy Markdown
Owner Author

@codex review

@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai review
@greptileai
@codex review

Please review current head 2a76ce190478bc79fca8f51361a90fabb70b2438 after the COM revocation failure-state fix.

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Rate Limit Exceeded

@amanthanvi have exceeded the limit for the number of chat messages per hour. Please wait 13 minutes and 33 seconds before sending another message.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: 2a76ce1904

ℹ️ 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".

Comment thread src/apprt/win32_terminal_handoff.zig Outdated
Register noctty as a Windows default-terminal provider: a classic
out-of-process COM server implementing ITerminalHandoff3, a MIDL proxy/
stub so the interface can be marshaled into an unpackaged server, an
adopted-PTY path that wires the handed-off ConPTY handles into termio,
and HKCU/HKLM registration with exact restore.

Windows' default-terminal pipeline is two-stage: conhost activates the
DelegationConsole CLSID (IConsoleHandoff) and that OpenConsole process
activates DelegationTerminal (ITerminalHandoff). noctty implements the
terminal half only and refuses to register unless a compatible console
half is already selected, because it cannot supply one.

Only ITerminalHandoff3 {6F23DA90-...} is implemented. It is the sole
terminal-handoff IID that Windows Terminal 1.24 package-registers, each
OpenConsole build calls exactly one compiled revision with no
negotiation, and v3 is the revision that lets the terminal create its
own pipes -- which matters because v1/v2 hand over host-created pipes
whose semantics do not match the overlapped named pipe that noctty's
libxev IOCP reader requires. v1/v2 are refused with E_NOINTERFACE and
one log line naming the minimum version.

The handoff supplies no HPCON, so an adopted PTY resizes by writing the
private ConPTY signal packet (opcode 8, u16 cols, u16 rows) to the
supplied signal pipe rather than calling ResizePseudoConsole.

Cross-process marshaling needs a registered proxy/stub. Windows
Terminal's OpenConsoleProxy.dll cannot be borrowed: it lives inside the
MSIX package and loading it from an unpackaged process fails with
E_ACCESSDENIED. noctty therefore ships its own proxy generated by MIDL
from Microsoft's IDL, checked in for x64 and arm64 so the build does not
require the Windows SDK.

Caller authorization uses CoImpersonateClient rather than the marshaled
server process handle, whose access mask we do not control.

Refs #130
Four review findings against the ITerminalHandoff3 server.

The wake-up message no longer carries a pointer. WM_WINHOSTTY_TERMINAL_
HANDOFF was posted to the embedding UI thread with the PendingSession
address in its LPARAM, and the loop accepted any nonzero value: thread
ids are enumerable and PostThreadMessageW crosses process boundaries at
the same integrity level, so any process on the desktop could hand the
default-terminal process an address of its choosing to dereference,
deinit and free. Sessions now live in a Server-owned table and the
message carries only an opaque identifier; an identifier we never issued
or already consumed is dropped without touching any session.

A failed CoRevertToSelf is no longer a failed handoff. The thread that
serves EstablishPtyHandoff is the process message loop, so leaving it
impersonating the COM caller means every later window, config read,
session write and child process runs under someone else's token.
Confirm the token is really still attached, try RevertToSelf directly,
confirm again, and panic if it is still there. Losing the adopted
windows is the cheaper failure: Windows falls back to a console window
for the pending launch.

Outstanding objects now keep the server alive. LockServer is optional in
the COM contract, so a client that created an ITerminalHandoff3 and had
not yet called EstablishPtyHandoff was invisible to the idle check and
could have the class revoked and the process exited underneath it. The
server counts live objects from creation to final release and reports
itself busy while any exist, alongside the lock count and the queue.

The console half is re-read immediately before the selection commit.
DelegationConsole and DelegationTerminal are independent values and the
user can change their default terminal in Settings mid-registration, so
the early check can be stale by the time the terminal half is written.
The recheck costs one registry read and fails before any selection
write, which leaves the previous terminal selected; a transactional
rollback would cost far more than the window is worth.
#130
#188

Keep the embedding server alive while clients hold factory references, roll back COM registration writes when the selection commit fails, and propagate an adopted PTY's initial resize failure through surface initialization.

Mark the known nonfunctional live handoff as experimental and describe proxy registration as configuration rather than an authorization boundary.
#130
#188

Keep backend cleanup with the caller until Termio initialization succeeds, avoiding a double free when an adopted session's initial resize fails.

Add the handoff proxy DLL to post-package Authenticode verification and Defender scanning, and update the fail-closed release contracts.
Roll registration back after any registry mutation failure, not only after the final terminal selection check. Keep the embedding server alive when adopted surface creation fails while COM clients or factory locks are still outstanding.

Refs #130.
Refs #188.
Restore the registration values observed at the start of an idempotent refresh when a later write fails. Serialize adopted signal lookup, resize writes, and close, and keep adopted-only state out of non-Windows compilation.\n\nRefs #130.\nRefs #188.
Reject a registration refresh when the final console-half re-read is no longer compatible. Only let the installer unregister the current-user handoff when both registered runtime paths still belong to that installation.\n\nRefs #130.\nRefs #188.
Require the downloaded portable proxy DLL for both architectures, validate its Authenticode signature through the shared pinned-signer policy, and raise the exact published signature evidence contract from eight to ten.\n\nRefs #130.\nRefs #188.
#130

Snapshot default-terminal registry state before unregistration starts. If a later registry operation fails, rebuild owned class values, restore completed shared mappings in reverse order, and restore the terminal selection last while preserving concurrent newer owners.
#130

Keep the COM registration cookie and tracked registration references when CoRevokeClassObject fails. The embedding idle-exit path now stays alive for a later retry instead of terminating while activation may still target the class object.
#130

Replace sequential thread-message identifiers with cryptographically random nonzero capabilities. Check each candidate against live queue entries while holding the queue mutex so forged same-integrity messages cannot predict and consume an active handoff.
@amanthanvi
amanthanvi force-pushed the issues/130-default-terminal-handoff branch from 2a76ce1 to 01bbf48 Compare September 1, 2026 03:03
@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai review
@greptileai
@codex review

Please review current head 01bbf482d75370a9731b821ac72e112af883a4d1, rebased onto main 26f26aedd924f017b8fbe3b7eb659265bb0c3de5, including the random pending-handoff capability fix.

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='01bbf482d75370a9731b821ac72e112af883a4d1'
prior='b5784f54bfde0f1f77d4b3f58b55bfd1622427a7'
base='26f26aedd924f017b8fbe3b7eb659265bb0c3de5'

echo '--- verify commits ---'
git rev-parse "$target" "$prior" "$base"

echo '--- commits after previous review ---'
git log --oneline "$prior..$target"

echo '--- changes since previous review ---'
git diff --name-status "$prior...$target"
git diff --stat "$prior...$target"

echo '--- full focused delta since previous review ---'
git diff --find-renames --unified=100 "$prior...$target" -- \
  src/apprt/win32_terminal_handoff.zig \
  src/apprt/win32.zig \
  src/pty.zig \
  src/termio/Exec.zig \
  src/Surface.zig \
  test \
  docs

echo '--- handoff module map ---'
ast-grep outline src/apprt/win32_terminal_handoff.zig --items all --view expanded || true

echo '--- pending capability and message flow ---'
rg -n -C 12 \
  -e '\b(PendingId|PendingSession|pending_sessions|pending.*capab|capability|random|drainPending|drainQueuedTerminalHandoffs|EstablishPtyHandoff)\b' \
  -e '\bWM_WINHOSTTY_TERMINAL_HANDOFF\b' \
  -e '\b(PostThreadMessageW|PeekMessageW|GetMessageW)\b' \
  src/apprt/win32_terminal_handoff.zig src/apprt/win32.zig

echo '--- ownership and cleanup transfers ---'
rg -n -C 10 \
  -e '\b(takeAdopted|adopted_session|closeHandoffPipeCopies|PendingSession|deinit\s*\(|release\s*\()\b' \
  src/apprt/win32_terminal_handoff.zig src/apprt/win32.zig src/Surface.zig src/pty.zig src/termio/Exec.zig

echo '--- test coverage for pending ids and message validation ---'
rg -n -C 8 \
  -e 'test ".*(pending|handoff|capab|random|replay|message)' \
  -e '\b(PendingId|PendingSession|pending.*capab|capability)\b' \
  src test 2>/dev/null || true

Length of output: 173285


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='01bbf482d75370a9731b821ac72e112af883a4d1'
prior='b5784f54bfde0f1f77d4b3f58b55bfd1622427a7'

echo '--- commit series and changed files ---'
git log --oneline "$prior..$target"
git diff --name-status "$prior...$target"

echo '--- exact changes after the prior review ---'
git diff --find-renames --unified=70 "$prior...$target" -- \
  src/apprt/win32_terminal_handoff.zig \
  src/apprt/win32.zig \
  test/windows \
  docs

echo '--- PendingQueue and Server complete implementation ---'
sed -n '250,450p' src/apprt/win32_terminal_handoff.zig

echo '--- handoff creation and ownership after PendingSession allocation ---'
sed -n '590,690p' src/apprt/win32_terminal_handoff.zig

echo '--- queue callback and its failure path ---'
sed -n '8020,8085p' src/apprt/win32.zig

echo '--- UI-thread terminal-handoff message dispatch ---'
rg -n -C 18 '\bWM_WINHOSTTY_TERMINAL_HANDOFF\b' src/apprt/win32.zig

echo '--- App shutdown, class revocation, and pending-session drain ---'
rg -n -C 20 '\b(drainPending|terminal_handoff_server|finishRevoke|revoke\(\)|isBusy\(\))\b' \
  src/apprt/win32.zig src/apprt/win32_terminal_handoff.zig

echo '--- capability-specific tests ---'
sed -n '1850,1920p' src/apprt/win32_terminal_handoff.zig

Length of output: 144580


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='01bbf482d75370a9731b821ac72e112af883a4d1'
prior='b5784f54bfde0f1f77d4b3f58b55bfd1622427a7'

echo '--- commit ancestry after the prior reviewed head ---'
git log --oneline --decorate --ancestry-path "$prior..$target"

echo '--- commits that changed pending-handoff code ---'
git log --format='%H %s' "$prior..$target" -- src/apprt/win32_terminal_handoff.zig src/apprt/win32.zig

echo '--- patches for only those handoff commits ---'
for commit in $(git log --format='%H' "$prior..$target" -- src/apprt/win32_terminal_handoff.zig src/apprt/win32.zig); do
  echo "### $commit"
  git show --format='format:%h %s' --unified=45 "$commit" -- \
    src/apprt/win32_terminal_handoff.zig src/apprt/win32.zig
done

echo '--- complete UI handler ---'
sed -n '2800,2885p' src/apprt/win32.zig

echo '--- all queue callback call sites ---'
rg -n -C 10 '\bqueueTerminalHandoff\s*\(' src

echo '--- all direct pending queue access sites ---'
rg -n -C 8 '\b(queuePending|takePending|pending\.(insert|take|drain)|nextCapabilityWithSource)\b' \
  src/apprt/win32_terminal_handoff.zig src/apprt/win32.zig

Length of output: 112930


@amanthanvi I completed a focused static review of 01bbf482d75370a9731b821ac72e112af883a4d1.

I found no actionable issue in the random pending-handoff capability change.

PendingQueue generates a nonzero random PendingId while it holds its mutex. It rejects IDs that exist in the live queue. take removes the entry under the same mutex. A consumed ID cannot return the same PendingSession again.

The UI message handler treats lParam only as an opaque lookup key. It drops unknown, zero, and replayed IDs without dereferencing message-controlled memory.

The PostThreadMessageW failure path removes the queue entry before it returns false. EstablishPtyHandoff then retains cleanup ownership. The successful message path transfers ownership to the UI handler, which deinitializes and destroys the claimed session once.

I did not run Windows runtime or COM integration tests in this environment.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 01bbf482d7

ℹ️ 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".

@amanthanvi
amanthanvi merged commit dae7922 into main Sep 1, 2026
7 checks passed
@amanthanvi
amanthanvi deleted the issues/130-default-terminal-handoff branch September 1, 2026 03:23
amanthanvi added a commit that referenced this pull request Sep 3, 2026
… mangling catalog (#129) (#164)

Fixes #129. Squash of issues/129-bundled-conpty at ecdc2c7; 4/4 review threads resolved; suite 4175/72/0; site tests pass; release gates now derive the signed payload list from one source (scripts/common.ps1) and verify the bundled ConPTY pair after publication. Closes #188's proxy-DLL release-gate residual.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants