Skip to content

Windows: ancestor walk refuses app container / capability SIDs, so 0.10.0 cannot start inside a containerized host #1533

Open

Description

@Kiborgik

Summary

On Windows, win_private_directory_tree_secure walks every ancestor of the runtime directory and refuses any that grants mutation rights to an identity it does not trust. App container and capability SIDs (S-1-15-2-*, S-1-15-3-*) are not on the trusted list, so when the process runs inside a containerized host application the walk refuses the container's own AppData and no endpoint can be created.

The result is that 0.10.0 cannot start at all from inside such a host, with no remedy available to the user: the offending ACE is on a directory the container owns, not on anything the user can edit.

Evidence

Instrumented build from main, printing ipc_validation_detail_buffer at the failing gate in cbm_daemon_ipc_endpoint_new:

DIAG gate=final dir=1 wide=1 names=1 pipe=1 mutex=1 tree_secure=0 err=0
     runtime_dir=C:\Users\petro\AppData\Local/cbm-daemon-c888acc3ae367a1e
DIAG detail=C:\Users\petro\AppData: DACL entry 0 grants mutation rights 0x000d0152
     to untrusted identity (other S-1-15-3-3557520199-3666692283-3112367039-3524159787-2791857073-3163583606-3692855932)

Every other condition in that gate passes. The refusal is on an ancestor (C:\Users\petro\AppData), not on the runtime directory itself.

Without instrumentation the only surfaced message is:

codebase-memory-mcp: secure CLI coordination could not be created (endpoint)

CBM_LOG_LEVEL=debug adds nothing, because the failure happens before the log layer is initialized.

Environment

  • Windows 11 26200, non-elevated standard token
  • codebase-memory-mcp 0.10.0 (also reproduced with a local build of main)
  • Host application runs the MCP server inside an app container, so its %LOCALAPPDATA% resolves to a container-owned object whose DACL carries the capability SID above
  • The same binary run from an ordinary terminal on the same machine works: that AppData has no such ACE

Version boundary

0.9.0 works from inside the same host. The ancestor walk is part of the strict permission gate introduced in 0.9.1-rc (#1416), so the regression window is 0.9.0 → 0.9.1-rc.

Why this differs from #1416

#1416 covers trees carrying Authenticated Users:(M) from a drive root, where both documented workarounds apply: move the install under the user profile, or strip the inherited grant. Neither works here.

  • The install is already under the user profile. The refused path is %LOCALAPPDATA% itself.
  • The runtime parent is not configurable. cbm_daemon_ipc_endpoint_new accepts a runtime_parent, but every production call site passes NULL (main.c:1486, 2399, 2559), and the only override is compile-gated behind CBM_ENABLE_TEST_SEAMS.
  • The ACE belongs to the container, not to the user, so there is nothing for the user to strip.

Proposal

Treat app container SIDs (S-1-15-2-*) and capability SIDs (S-1-15-3-*) as trusted in the ancestor walk only, leaving the final runtime directory under the existing exact-owner rule.

The argument for it: neither is a login-capable account. A grant to a capability SID is a statement about which packaged identity the OS will hand the token to, not a second user who can tamper with the tree. That is the threat the gate exists to stop, and it is not what these ACEs represent.

Two things I could not settle myself, which is why this is an issue rather than a PR:

  1. Whether an arbitrary local process can assume a chosen capability SID. If it can, the exemption is not safe and this should stay refused, in which case the request becomes "make the runtime parent configurable" instead.
  2. Whether the exemption should be ancestors-only. That is sufficient for this failure, and it is the narrower change.

Happy to write the patch either way once you have ruled on the direction.

Activity

  1. mwgrant21 commented on Aug 11, 2026

    @mwgrant21

    Independent reproduction on 0.10.2, with evidence that the trigger is broader than the title suggests — and that this and #1529 are the same defect seen from two angles.

    Environment: Windows 11 26200, non-elevated standard token, 0.10.2, GitHub release archive.

    Same gate, same symptom. daemon status fails with only:

    codebase-memory-mcp: secure daemon endpoint could not be created
    

    and CBM_LOG_LEVEL=debug adds nothing, exactly as you describe — the failure precedes log-layer init, so there is no diagnostic surface without an instrumented build.

    The difference: the untrusted identity is not an app container or capability SID. On this machine the offending ACE on %USERPROFILE%\AppData is a non-inherited grant to a local group:

    Identity                          Rights               Inherited
    <HOST>\CodexSandboxUsers          Modify, Synchronize  False      <-- on AppData itself
    S-1-5-21-<redacted>               Modify, Synchronize  False
    NT AUTHORITY\SYSTEM               FullControl          True
    BUILTIN\Administrators            FullControl          True
    <HOST>\<user>                     FullControl          True
    

    No S-1-15-2-* or S-1-15-3-* SID is present anywhere on AppData. So the ancestor walk in win_private_directory_tree_secure is refusing an ordinary local group, which means the fix cannot be limited to trusting app container / capability SIDs — the general shape is any non-inherited mutation grant on an ancestor of the runtime directory, from any identity not on the trusted list.

    This connects #1529. The OpenAI Codex desktop sandbox creates the local CodexSandboxUsers group and grants it Modify on several shared paths. On this machine that is %TEMP%, ~/.cache, and %USERPROFILE%\AppData. The consequence is a two-stage failure that reads as two unrelated bugs:

    1. The installer's activation transaction fails with acl-grants-cross-account-mutation on the staging dir — this is Windows installer fails with CodexSandboxUsers ACL in %TEMP% #1529. Staging somewhere private clears it.
    2. Startup then fails at the endpoint gate on the AppData ancestor — this issue. CBM_CACHE_DIR does not help, because the runtime directory is %LOCALAPPDATA%\cbm-daemon-<hash>, whose ancestor chain is untouched by relocating the cache.

    So #1529's workaround gets a user past the installer and straight into this, with a different and much less informative error. Worth noting on that issue too — a user who applies the documented temp workaround will reasonably conclude the fix didn't work.

    Why this is not user-remediable here either. Your original report notes the ACE sits on a directory the container owns. Same conclusion by a different route: the grant belongs to another vendor's sandbox, on the user's own AppData. Removing it to satisfy this tool would risk breaking Codex CLI's sandbox — trading one working tool for another.

    Happy to run an instrumented build and post the ipc_validation_detail_buffer output for the local-group case if that would help distinguish it from the capability-SID path.

  2. mwgrant21 commented on Aug 11, 2026

    @mwgrant21

    Resolved on my machine, and the cause narrows usefully: the ACEs were orphaned by an uninstall.

    Follow-up to my earlier comment. Codex turned out not to be installed on this box at all — no executable, no ~/.codex/ config, no uninstall registry entry. What remained were the CodexSandboxUsers local group and its ACL grants, left behind when the app was removed.

    Each of the three affected directories carried exactly two non-inherited Modify grants:

    Path Grant 1 Grant 2
    %USERPROFILE%\AppData CodexSandboxUsers unresolvable SID
    %LOCALAPPDATA%\Temp CodexSandboxUsers unresolvable SID (different)
    %USERPROFILE%\.cache CodexSandboxUsers unresolvable SID (different again)

    The second grant in each case resolves to no principal on the machine — a different foreign-domain SID per directory, consistent with per-install sandbox accounts orphaned across several install/uninstall cycles.

    Removing those six ACEs fixed both failures. install.ps1 then completed with no custom temp directory, and daemon status returned normally (daemon: not running) instead of the endpoint error. Indexed a 128-file repo immediately afterward with no further changes. CBM_CACHE_DIR was not needed and has been reverted to the default.

    Two things this suggests for the fix:

    1. An unresolvable SID is a strictly safer case to tolerate than a live one. An ACE naming a principal that no longer exists cannot actually be exercised by anyone, so refusing on it is a false positive with no security benefit. Filtering ACEs whose SID fails to resolve would clear a meaningful share of real-world machines without loosening the guarantee at all.

    2. The diagnostic is the expensive part. Reaching this took reading source, an ancestor-by-ancestor ACL walk, and finally this issue — the surfaced message names neither the path nor the identity. Even without any policy change, printing the refusing ancestor and the offending SID in the default error (as your instrumented build already computes into ipc_validation_detail_buffer) would turn a multi-hour investigation into a one-line fix. That alone would likely close most reports of this shape.

    Happy to test a build that relaxes the unresolvable-SID case if useful.

  3. rtellez91 commented on Aug 12, 2026

    @rtellez91

    This is also reproducible with the released Windows UI-capable binary v0.10.2 when the installation and cache are kept on a non-system drive.

    Environment

    • Windows x64
    • codebase-memory-mcp v0.10.2
    • executable: D:\tools\codebase-memory-mcp\codebase-memory-mcp.exe
    • no user-local duplicate installation
    • installation and cache kept on D:

    Reproduction

    codebase-memory-mcp --ui=true --port=9749

    Result:

    codebase-memory-mcp: secure daemon endpoint could not be created
    

    The same happens with daemon-backed CLI commands such as codebase-memory-mcp cli list_projects. Moving the cache to a D: location gets past the cache-private check, but the daemon endpoint still cannot be created. Before moving the cache, the failure identified an inherited Authenticated Users mutation grant on D:\tools.

    Expected behavior

    A supported custom runtime/IPC directory should allow the complete installation to operate from a user-controlled non-system drive, without changing unrelated ACLs on the existing install tree or relying on %LOCALAPPDATA%\codebase-memory-mcp.

    Please consider exposing a production CBM_RUNTIME_DIR/runtime-parent setting or an equivalent CLI option. In v0.10.2, the runtime parent appears to remain tied to Windows Local AppData even when the executable and cache are configured elsewhere.

    This seems related to #1416, but the key request here is the configurable runtime parent described in this issue.

  4. added
    bugSomething isn't working
    securitySecurity vulnerabilities, hardening
    editor/integrationEditor compatibility and CLI integration
    ux/behaviorDisplay bugs, docs, adoption UX
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    on Aug 12, 2026
  5. DeusData commented on Aug 12, 2026

    @DeusData
    Owner

    Thank you @mwgrant21 and @rtellez91 for narrowing the additional cases. The current product paths still have no runtime-parent setting: the only environment override is the test-only CBM_TEST_DAEMON_RUNTIME_PARENT, and production endpoint call sites otherwise pass NULL.

    The narrower remediation is now tracked explicitly in #1574 and proposed in #1576 as CBM_RUNTIME_DIR, with the same private-directory validation retained at the selected location. That avoids broadening the trusted-SID policy merely to make one ancestry usable. The implementation PR is security-sensitive and also needs to be unstacked from #1561 before review, but these reports are now cross-linked to the concrete product path. Thank you for testing the ACL repair and relocation cases separately.

  6. ozkanmelen commented on Aug 13, 2026

    @ozkanmelen

    Still reproducible in v0.10.3 — and the offending ACE is created by the Claude Desktop app itself

    Fresh data point from a clean install on Windows 10 Pro 10.0.19045 (x64):

    • install.ps1 --skip-config succeeded (v0.10.3, checksum verified).
    • Every mode fails identically, before logging activates:
    • The ancestor walk trips on an explicit full-control AppContainer ACE on %USERPROFILE%\AppData, inherited down the tree:
    C:\Users\<user>\AppData  S-1-15-2-3557520199-3666692283-3112367039-3524159787-2791857073-3163583606-3692855932:(F)
                             S-1-15-2-3557520199-…-3692855932:(OI)(CI)(IO)(F)
                             <user>:(OI)(CI)(F)
                             NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
                             BUILTIN\Administrators:(I)(OI)(CI)(F)
    
    • Via HKCR\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Mappings, that package SID resolves to the Anthropic Claude desktop app (DisplayName Claude, moniker claude_pzs8sxrjxfjjc). Its sandbox grants itself this ACE on install, so the condition is not exotic: it exists on any machine running Claude Desktop — likely a large share of this project's MCP audience, given Claude Code is one of the auto-configured clients.

    Since the ACE belongs to a legitimately installed host application's AppContainer and end users cannot reasonably remove it, +1 for the proposed fix of treating S-1-15-2-* / S-1-15-3-* as trusted during the ancestor walk (or providing an explicit override).

  7. DeusData commented on Aug 13, 2026

    @DeusData
    Owner

    @ozkanmelen, thank you for the clean v0.10.3 reproduction and for resolving the SID back to the installed Claude Desktop AppContainer. That is a material distinction from the orphaned-SID cases: this ACE belongs to a live, relevant host application and cannot reasonably be removed as user remediation.

    This leaves a real security-policy choice rather than a missing diagnostic. The existing CBM_RUNTIME_DIR direction in #1574/#1576 preserves the current trust rule by moving the private endpoint to a usable ancestry. Exempting S-1-15-2-* or S-1-15-3-* during the ancestor walk would be more automatic, but it changes which mutation-capable principals the endpoint validator trusts and requires a dedicated security decision.

    I am keeping this priority/high and security-labeled. Triage will not choose between those threat-model options. No additional diagnostics are needed from you; thank you for establishing that the v0.10.3 failure affects a normal Claude Desktop installation rather than an exotic container setup.

  8. DeusData commented on Sep 26, 2026

    @DeusData
    Owner

    Thank you, @Kiborgik, for the instrumented diagnosis, and @mwgrant21, @rtellez91 and @ozkanmelen for the additional cases! Tracing the SID back to the Claude Desktop AppContainer is what settled the policy question.

    Two changes shipped in v0.10.5:

    An orphaned local-group grant, such as leftover CodexSandboxUsers ACEs, is still refused on purpose, because trusting an arbitrary live group would weaken the check. For that case, and for the D:\ install layout, CBM_RUNTIME_DIR is the intended remedy. Could you confirm that v0.11.0 now starts inside your containerized host?

  9. added
    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then close
    on Sep 26, 2026
  10. Kiborgik commented on Sep 26, 2026

    @Kiborgik
    ContributorAuthor

    I had no problem with this since 0.11.0 @DeusData

  11. github-actions commented on Sep 26, 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.

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

    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then closebugSomething isn't workingeditor/integrationEditor compatibility and CLI integrationpriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.securitySecurity vulnerabilities, hardeningux/behaviorDisplay bugs, docs, adoption UXwindowsWindows-specific issues

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions