Skip to content

Windows: activation ancestor-walk fails unconditionally on stock/default ACLs (no OS-level denial in ProcMon trace) #1856

Open

Description

@zaferavci1

Summary

On Windows, install.ps1 fails at the "activation" step with activation could not reserve exclusive access; no activation was committed. — regardless of which directory is used as --dir, and independent of the CBM_CACHE_DIR location. This looks like the same "ancestor walk" ownership/ACL check discussed in #1830 (Linux/Docker userns-remap) and #1533 (Windows containers), but triggered here on a completely standard, non-containerized Windows 10 install by Windows' own out-of-the-box default ACLs.

Environment

  • OS: Windows 10 Pro, build 10.0.19043 (21H1) — no TLS 1.3 support in schannel
  • codebase-memory-mcp: v0.10.8 (windows-amd64)
  • Running fully elevated as a local Administrator (confirmed SeCreateGlobalPrivilege and SeDebugPrivilege enabled, IsInRole(Administrator) = True)
  • No antivirus/EDR installed (WinDefend service absent, SecurityCenter2 empty, no known AV process running)

Repro

irm https://raw.githubusercontent.com/DeusData/codebase-memory-mcp/main/install.ps1 -OutFile install.ps1
# (had to patch line 11 to `Tls12` only -- see "Separate issue" below)
.\install.ps1 --dir=C:\Users\<user>\codebase-memory-mcp

Output:

Downloading codebase-memory-mcp-windows-amd64.zip...
Checksum verified.
Extracting...
Verified candidate: codebase-memory-mcp 0.10.8
error: activation could not reserve exclusive access; no activation was committed.
error: this is NOT a running-session problem — the reservation itself failed (coordination lock, leftover state, or permissions). ...

This reproduces identically:

  • with a brand-new, freshly created --dir (clean ACL: owner+SYSTEM+Administrators+the account only, no other principals)
  • with a brand-new CBM_CACHE_DIR pointed elsewhere (rules out leftover cache state)
  • across three separate target directories

Root cause (confirmed via Process Monitor)

I captured a full ProcMon trace across a failing run. Every single Windows API call the process makes succeeds (CreateFile, the 296 MB staged-candidate write, QuerySecurityFile, etc.) — there is no ACCESS DENIED, SHARING VIOLATION, or any other OS-level denial anywhere in the trace. The process itself deliberately deletes its own staged candidate (SetDispositionInformationEx with FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS | FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK) and exits 1 right after inspecting the DACL of the target directory and its parent. This is an application-level decision, not an OS failure — i.e. the same class of check as posix_directory_parent_secure in #1830, just walking the Windows ACL ancestor chain instead of POSIX uid ownership.

I isolated two concrete triggers of the same check family, both on default, unmodified Windows ACLs:

  1. --dir=C:\<fresh-dir> (parent = user profile root, e.g. C:\Users\Administrator): fails with the generic message above. The profile root carries Windows' standard (A;;0x1200a9;;;WD) ACE — Everyone/World, read-only, not inherited by children — which is present on essentially every Windows user profile by default.
  2. --dir=C:\<fresh-dir-directly-under-C>: fails with a specific error instead:
    error: failed to stage install candidate: activation transaction I/O failed: acl-grants-cross-account-mutation to S-1-5-11 (os 0) at C:/<dir>
    
    S-1-5-11 is Authenticated Users — the account that gets create file/create subfolder rights on C:\ by default on every Windows install since Vista ((A;OICIID;0x1301bf;;;AU) inherited from the drive root). This is not a hardened/misconfigured machine; it's Microsoft's stock default.

Since (1) and (2) fire on stock Windows defaults with no containerization, no third-party ACL tooling, and no other logged-in principals actually able to write anything of consequence, the ancestor-walk check appears to be unconditionally too strict for Windows: any non-owner principal anywhere in the ancestor chain — even a read-only Everyone ACE, even the standard AU-create-only ACE on C:\ — trips it. This would make normal (non-Server-Core, non-freshly-re-ACL'd) Windows installs unable to complete activation at all.

Separate/related issue: TLS 1.3

install.ps1 line 11 hardcodes:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13

On Windows 10 (schannel has no TLS 1.3 support before Windows 11 / Server 2022), setting this bitmask makes HttpClientHandler's GetAsync fail outright with WebException: İstek durduruldu: SSL/TLS güvenli kanalı oluşturulamadı. (SSL/TLS secure channel could not be created), even though the same machine's Invoke-WebRequest to the identical URL succeeds fine. Dropping the -bor Tls13 fixes it. Might be worth an OS-version check (or a try/fallback) before setting Tls13 in the bitmask, since combining an unsupported protocol flag with a supported one causes a hard failure rather than a graceful fallback here.

What I've ruled out

  • Leftover lock/cache state (tested 3 different cache paths, all fresh/empty)
  • File permissions on the leaf target directory (confirmed writable, full control)
  • AV/EDR interference (none installed on this machine)
  • Restricted/UAC-filtered token (fully elevated, SeCreateGlobalPrivilege + SeDebugPrivilege enabled)
  • Port conflicts on 9749 (nothing listening)
  • --dry-run (works fine — it's specifically the write/activation path that fails)

Suggested fixes

Happy to provide the full ProcMon .pml/CSV or run further diagnostics if useful.

Activity

  1. added
    bugSomething isn't working
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    securitySecurity vulnerabilities, hardening
    on Sep 5, 2026
  2. added a commit that references this issue on Sep 12, 2026
  3. DeusData commented on Sep 12, 2026

    @DeusData
    Owner

    Thank you for this — the ProcMon trace, the decoded S-1-5-11, and the TLS bisect are the reason all of this was actionable rather than guesswork. Three separate things were in your report, and they landed differently, so here is each one honestly.

    1. The TLS 1.3 failure is fixed — aacf96a2

    You were exactly right. install.ps1 set Tls12 -bor Tls13 unconditionally. Windows 10's schannel has no TLS 1.3 (it arrives with Windows 11 / Server 2022, build 20348), but .NET Framework 4.8 still defines the enum value — so the assignment succeeds, nothing warns, and the first request dies with "Could not create SSL/TLS secure channel". An unsupported flag in that bitmask is a hard failure, not a downgrade.

    The protocol set is now computed: Tls12, plus Tls13 only on build 20348+, with an enum-name probe so the script still parses on .NET 4.7. Verified on a Windows box with the shipped parser — on Windows 11 it keeps Tls12, Tls13; forcing build 19045 through the same expression yields Tls12 alone.

    2. The refusal now names the check that refused — 73490637

    This is the part we owe you an apology for. Your output was:

    error: activation could not reserve exclusive access; no activation was committed.
    error: ... Check the errors above ...
    

    …and there was nothing above. That was our bug, not a gap in your report: the code path that validates the cache, rendezvous and log directories printed that message bare, bypassing the helper that appends the detail naming the failing component. So the binary computed the answer you needed and then discarded it before printing. You could not have diagnosed it from the outside, and no amount of ProcMon would have shown it.

    Fixed, plus a contract test that guards the whole class — any future code path that prints a refusal without attribution now fails CI.

    3. The C:\<dir> case refuses on purpose — but now tells you how to fix it

    Your analysis of the stock (A;OICIID;0x1301bf;;;AU) ACE was correct, and we looked hard at simply tolerating it. We decided not to, and it is worth explaining why rather than just saying no.

    That mask includes FILE_ADD_FILE and DELETE on the directory. The installed binary itself is safe regardless — it gets an owner-only, SE_DACL_PROTECTED DACL — but the directory is not the binary. install.ps1 copies itself there and cbm update tells you to run that copy; the install directory goes onto your PATH; and the application directory is first in the DLL search order. Tolerating that ACE would let any authenticated account on the machine plant a DLL beside the executable or edit the updater we tell you to run.

    So it still refuses — but the message no longer sends you down a dead end. It now says whether the grant was inherited, because the remedy differs and the wrong one silently does nothing: icacls <dir> /remove:g <sid> cannot remove an inherited ACE. For your case the working command is:

    icacls C:\<dir> /inheritance:r /grant:r "%USERNAME%":(OI)(CI)F
    

    Installing under the default %LOCALAPPDATA%\Programs\codebase-memory-mcp avoids it entirely, since the profile already isolates the ACLs.

    What would help us: one re-run

    Case 1 — the failure with --dir under your profile root — is the one we still cannot explain. Your C:\Users\<user>\... directory had clean ACLs, so it should not have tripped the ACL check at all, and until now the error told us nothing. With the fix above it will name the exact check that refused.

    If you have the time once v0.10.9 ships, could you re-run that original repro and paste the new error? That one line should be enough to finish this. Labelling awaiting-reporter only to track that the ball is with you — not a nudge.

    Thanks again for the depth here; it turned into three fixes and one design decision we had to think properly about.

  4. added
    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then close
    on Sep 12, 2026
  5. github-actions commented on Sep 12, 2026

    @github-actions

    Thanks for the report! To move this forward we need a bit more so we can reproduce it ourselves:

    • the codebase-memory-mcp --version you're on
    • the exact steps or command you ran
    • a public repo (or a small dummy snippet) that shows the problem — please don't paste proprietary code

    Once that's here we'll pick it straight back up. Heads-up: issues left awaiting-reporter are automatically closed after a few weeks of silence, but a comment reopens the door anytime.

  6. zaferavci1 commented on Sep 14, 2026

    @zaferavci1
    Author

    Thanks for the detailed breakdown and the fixes! I'll keep an eye out for the v0.10.9 release, re-run the repro under my user profile (C:\Users<user>...), and post the new error log here as soon as it's out.

  7. removed
    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then close
    on Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.securitySecurity vulnerabilities, hardeningwindowsWindows-specific issues

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions