codebase-memory-mcp v0.10.5
An install-and-startup release. If v0.10.4 refused to install for you, refused to write your
config, or told you something that turned out not to be true, this is the one to take.
Several of these are cases where cbm refused its own work — an installer directory our own
binary then rejected, a config entry we wrote ourselves and later called foreign, an error
message that named the wrong directory and sent people to inspect a file that was fine.
Windows: installs that refused themselves
The installer was creating a directory our own binary then rejected. install.ps1 made its
staging directory with New-Item, which inherits whatever %TEMP% carries. The downloaded binary
validates its own directory and refuses inherited cross-account mutation grants — so we handed our
own check a directory we had just made wrong. Measured on a Windows VM: a fresh staging directory
came up unprotected with five inherited ACEs; it now carries a single owner-only entry.
That is the acl-grants-cross-account-mutation to S-1-5-21-… failure. Reported by @RoccoZero,
@aecesr, @Kiborgik and @nasodaengineer, whose corrections stopped us fixing the wrong
thing twice — @Kiborgik warned that keying on the sandbox group's name would miss a synthesized-SID
variant, and @nasodaengineer disproved an off-by-one-RID theory by showing the ACE really was present
on the inherited DACL. @spam0115 independently wrote a working installer whose decisive line was
the same icacls /inheritance:r, and handed it back to us rather than keeping it.
Sandboxed applications no longer block the daemon. The ancestor walk refused any
mutation-granting entry on %LOCALAPPDATA%, including AppContainer identities left by ordinary
installed software. On reported machines the blocking entry resolved to a shipping desktop
application — nothing a user could reasonably remove. Ancestor components now tolerate AppContainer
package and capability identities; the private runtime directory keeps demanding the exact current
user, unchanged. Approach and the ancestor-only boundary follow @mlandolfi90's work.
Startup: when the daemon could not be reached
-
The rendezvous directory can be relocated in a shipped build via
CBM_RUNTIME_DIR. Previously
the only relocation hook was compiled out unless test seams were enabled — so a test build
started where the product build did not, and when the default ancestry was unacceptable
every invocation failed,config listincluded, leaving no way to reach the settings surface
and fix it. (#1537, #1574, #1621)@tmonestudio and @Carnival-z each proposed exactly this, independently, before we built it
— @tmonestudio in #1574 with the ancestry walked and the offending component named, @Carnival-z in
#1621 after finding thatCBM_CACHE_DIRmoves the cache but never the rendezvous. Both had opened
pull requests reaching the same conclusion this change implements; the merged commit credits
@tmonestudio by name for the approach and the variable name. -
Refusal messages now name the directory that actually refused. The ancestor walk validates the
directory it is already in, but printed the name of the child about to be entered — so #1537 was
told.cachewas at fault when the home directory was refusing, and #1621 was told
cbm-daemon-501when/private/tmpwas. Both reporters inspected exactly what we named, found it
clean, and said so. They were right, and we kept pointing at it for weeks. The message now
names the containing directory and says explicitly not to check the one it used to blame.
Errors that name what actually refused
Three fixes, one complaint: our messages did not say enough to act on.
secure CLI coordination could not be created (endpoint)now prints the validation detail —
the directory and the rule that refused. Four separate reporters hit that message; one built an
instrumented binary to discover a single ACE was the cause.- A refused agent-config write says what the target is — whether it exists, and what kind of file
it is. Nine distinct fail-closed conditions previously collapsed into one indistinguishable failure. - Neither prints
errno. It is called from 119 sites and can be stale, which is exactly how an
earlier release handed a reporter a fabricated "file not found" for a permission decision.
Config files we wrongly refused to touch
- An MCP entry your client annotated is no longer treated as foreign (#1630, #1582). OpenCode
writes"enabled": truebeside thecommandandtypewe write; our ownership check demanded an
exact key-set match, so a three-key entry with two recognised keys was classified as somebody
else's and left alone — we refused to touch an entry we had written ourselves. In one reporter's
file every MCP server carried the key, so this is OpenCode's normal shape, not an unusual hand-edit:
anyone who has ever toggled a server on or off in the UI was hit. - YAML values containing
*or&are no longer mistaken for aliases and anchors (part of #1631).
Prose asterisks, a glob inside a description, a kaomoji — all were refused as if they began a node.
The test is now positional. Root-caused by @rg6304, who reproduced it in isolation, read the
source, and corrected our hypothesis: the constructs we had guessed appear nowhere in the failing
file. This is one of four constructs in that report; the rest are still open.
Data loss and diagnosis
uninstall --helpno longer performs a real uninstall (#1038). It removed the binary and every
agent configuration for someone asking what the command does. The guard is checked before argument
parsing, so a-yelsewhere on the line cannot auto-confirm the destruction being prevented.- A crashed worker's log now survives and names its run (#1145, #1130, #1132, #1450, #1133, #1070).
Six reports ended at the same dead end: the supervisor prints the log path and the file is 0 bytes,
so the one diagnostic that would explain the failure was silenced by the failure itself. cli <tool>no longer blocks forever reading standard input (#1359) for a tool that declares no
arguments.--jsonand--progressare stripped before the stdin fallback is reached, so
cli list_projects --jsonhung too — the reporter's correction that "no flags" was not the trigger
was right.
Reliability
- Spawn retries no longer grow without bound (@moffermann). The clamp we shipped could never
fire, while the doubling it was meant to bound ran free — a future budget increase would have meant
a 40-second wait where the retry exists to avoid exactly that. Both current budgets are unchanged. - Two concurrent sessions can no longer be handed the same temporary directory
(@ertankucukoglu) — a process-global buffer in the Windows temp-directory helper. - Instrumented test builds now know they are instrumented (@moffermann). Four places asked
that question four different ways, so some sanitizer lanes measured instrumented code against
native timing budgets.
Correctness
check_index_coverageno longer reports every path as unavailable when a repository has more
than 2,000 ignored files (@SunneeYang). A truncated catalog was treated as proof that no
per-path answer existed, including for paths with a current hash record.- Go routes built by string concatenation now resolve (@AmirF194). A handler registered with
base + "/orders"produced no usable route path; the literal half is now recovered, and a path
assembled from a variable is still left unresolved rather than fabricated. - Cypher queries repeating a variable-length node no longer double it (@jstar0), matching the
unification the fixed-length path already did. - A Windows publish failure says what actually failed. The atomic publish discarded the Win32
error and reported a bareerrno, and one path returnedPERSIST_FAILEDwith no log line at all —
which is how a reporter with an ACL problem was told to check that their repository path existed. - Cross-repo intelligence works again on any project that has ever recorded a parse miss
(@vitaliy-shatskiy, #1609). Indexing writes an internal<name>::missedrow into the same
database, and the validation required exactly one row over all of them — so such a project was
rejected as "not indexed", both as source and as target, with no workaround available to the
operator. The same defect was fixed forlist_projectsin #1044; this site never learned it.
Performance
- Windows code search prefilters simple suffix globs (@ertankucukoglu), so a
*.extfilter no
longer scans and then discards. The pattern reaching the shell stays behind an allowlist, and the
original post-scan filter is retained as a second guard. - A failed search launch no longer leaks its compiled path filter (@ertankucukoglu).
search_codecan report per-phase timings on request (@ertankucukoglu) —scope_ms,
scan_msandenrich_ms, behinddebug: true. Default output is unchanged, and the measurement
itself is skipped unless asked for, so nothing is paid for by callers who do not want it.
Updating from 0.9.x is possible again
If you are on 0.9.x and update has been refusing to install, this is the release that fixes it
(#1134). v0.10.0 consolidated to one archive per platform, so the codebase-memory-mcp-ui-* names
that 0.9.x updaters ask for stopped existing and those updaters 404'd. We fixed that by publishing
byte-identical copies under the legacy names — but that step runs after checksums.txt is
generated and signed, so the aliases never appeared in it, and an updater that verifies the name it
asked for refused what it could not check:
warning: codebase-memory-mcp-ui-darwin-arm64.tar.gz not found in checksums.txt
error: refusing to install an unverified download
We fixed the 404 and moved the failure to verification. Confirmed on v0.10.4: eight ui-* archives
published, none of them listed. Their digests are now emitted before the attestation step, so the
attested checksum file covers both names — no new bytes, and an alias is a copy, so its digest is by
construction the one already computed. Reported by @AmooAti, whose installer was behaving
correctly the whole time.
Also
- The test suite no longer writes into the developer's real agent configuration (@phyrexia).
- A release-gate verdict no longer depends on a live network fetch. The licence provenance audit
downloaded the canonical Apache-2.0 text while running and byte-compared it, so a failed request
reported a licence discrepancy that did not exist — reddening an unrelated contributor's pull
request for over two weeks. It is now pinned by digest. - MCP envelope fuzz coverage for wrong JSON types (@lukiod).
- Release notes now report the verdict for the binary actually shipped, and no longer claim a minimum
engine count that stopped being true.
Still open — not fixed in this release
Being explicit, because several of these have been open a long time and shipping quietly around them
would be worse than saying so:
- #635 — on Windows, clients using the Python MCP SDK never complete the handshake, so no tools
appear. Open 50 days. - #1633 — Codex refuses
config.tomlentries cbm itself wrote in an earlier version. The
OpenCode half of this pattern is fixed above; the Codex/TOML half is not, and we have asked the
reporter for the exact entry shape. - #1631 — three of the four YAML constructs in that report remain unsupported.
- #1620 — on one Windows host, indexing always fails with a generic
Pipeline failed: our own
protected cache-directory DACL breaksMoveFileExW(REPLACE_EXISTING). Only the diagnosis half is
fixed here — the publish failure now reports the real Win32 error instead of a discardederrno,
so the next person is not sent to check that their repository path exists. The behaviour itself is
unchanged: a fix broke daemon startup in testing and was pulled rather than shipped half-right. - #1650 — where another agent's sandbox (OpenAI Codex CLI) has given a local group
profile-wide ACEs, the daemon still refuses to start. The tolerance added above covers AppContainer
identities, which a process cannot escape; an ordinary local group names a principal that really can
be exercised, so it is still refused deliberately.CBM_RUNTIME_DIRis the intended way out — point
it at a directory whose ancestry passes. - #1632 —
updatecan print aninstall.shpath that does not exist, with no fallback. A fix is
in review; it did not make this release. - #1654 — a large tree that v0.9.0 indexed in ~13 minutes fails to index on v0.10.4, reaching
about 1% in 45 minutes and then dying on allocation failures. Reported today and not yet
reproduced by us; nothing in this release targets it. If you are on a large repository, this
release does not change that behaviour. One thing here does help though: a crashed worker's log now
survives, so the next run should produce a diagnostic instead of an empty file. - #1343, #630, #1083 — reproduced by their reporters, not yet by us.
If you are hitting one of these, the issue is the best place to follow it; we would rather point you
at an open ticket than imply it is handled.
Upgrade
- Binary installs: download below, or re-run
install.sh/install.ps1. - Package managers: update through them as usual.
- No index, cache, or config migration.
Full changelog: v0.10.4...v0.10.5
Security Verification
Every binary published below was scanned by VirusTotal before smoke and soak testing, and the verdict for the exact shipped bytes is linked per product (decisive engines: 60–66).
1 shipped binary carried only the documented single Microsoft machine-learning !ml result; no other decisive engine reported malicious or suspicious.
Correction (post-publication). The table above originally reported
linux-amd64as carrying a Microsoft!mldetection anddarwin-arm64as clean. That was taken
from the pre-selection candidate scan. The final scan of the exact shipped bytes, run minutes
later, returned the opposite verdicts for those two files, and the table now reflects that
final scan.Nothing about the binaries changed between the two scans — same bytes, same hashes. Microsoft's
machine-learning engine simply returned a different answer for each of them within the same hour,
in opposite directions. The other six products were stable and clean in both scans.This is worth stating plainly rather than quietly editing: a
!mlverdict is a probabilistic
classifier output, not a property of the file, and we cannot make it stable from our side. Every
hash above is linked so you can check the current verdict yourself — and it may well differ again
from what we recorded.
| Product | Shipped binary | VirusTotal verdict |
|---|---|---|
linux-amd64
| 82750cd15af0165241c6a22e783eaf8c3e57575aed86aaf2a84a866a15de7ab6
| clean |
linux-arm64
| f944b83584fd9b6e83f5199fd71bf628218516fe0af2e8ced9376ee1367f19da
| clean |
linux-amd64-portable
| dfd7db016354f223cb8c8a22d8c4ceb418b51b8b4bce541669ff32288731ad03
| clean |
linux-arm64-portable
| 235258163aae1e573ae61933fee31f1e023d593211acc1243507401727f6f9a8
| clean |
darwin-amd64
| 785862e49b5939b6093ca150d322fb6666a237dec93c2d74f1c632f2edcc3f1a
| clean |
darwin-arm64
| 6d3c5be6412008c039b1dbaadc2a295a032bbcae52671e71ee13233b665495a8
| Microsoft !ml
|
windows-amd64
| f8aee1683f94f7598ae70640a588f27d51811433f046b39c1faf186c17010739
| clean |
windows-arm64
| c3a91dcab960250521a31dd831e2aad6785971db40c013513d37f17eda95b3a0
| clean |
Selection is tuple-local and defaults to stripped. The selected executable SHA-256 was verified again after packaging; archive containers were not redundantly submitted to VirusTotal. Their hashes remain available in checksums.txt.
Durable evidence: candidate provenance, candidate VirusTotal results, selection decisions.