Skip to content

v0.24.0 - let the estate ask, instead of guessing on its behalf

Choose a tag to compare

@AmanSK5 AmanSK5 released this 02 Sep 14:47
· 45 commits to main since this release
965cdbe

Every list in 0.23.0 was one I wrote by hand after reading process names off a screen. That is the wrong direction — the estate had already observed all of them, and this platform already has the machinery to ask about what it does not recognise. It just was never wired to this surface.

Discovery does it for domains: unknown host, classify, queue, a human decides. The receiver does it for MCP servers, automatically on ingest. CandidateIn has carried a kind field the whole time.

_CANDIDATE_KINDS = ("domain", "mcp_server")        before
_CANDIDATE_KINDS = ("domain", "mcp_server", "process")   now

Processes become questions

The receiver derives every process name the registry can account for — CLI binaries, desktop app_names, Windows exe_names, allowed_processes — and anything else reaching a model host becomes a candidate carrying its evidence, deduped by name so one script on forty machines is one question rather than forty.

SearchHost.exe, Fireflies.exe and stable should have arrived that way rather than as a commit.

Those sets now live in three services that share no library, so there is a parity test asserting they do not drift — verified failing on a one-word divergence before it passes. Same class of bug as the collector dedupe key: a property that must hold in three places, checked in one.

inference_domains needed unpicking before it could help

build.py refused the field on anything but form: ide, and that rule was correct — the IDE signal was its only reader, and inert configuration reads as working. So the second reader came first: the agentic view now uses it to tell a tool being fetched from one being used. Only then is relaxing the rule honest rather than convenient.

Then the backfill, 2 tools → 15. Safe polarity throughout: a tool that declares nothing is never filtered, so a growing registry can never silently delete findings.

cursor  cursor.com       -> dropped (a fetch, not a run)
cursor  api2.cursor.sh   -> kept   (a model answered)
claude  anthropic.com    -> dropped
claude  api.anthropic.com-> kept

A scheduled job is now recognised three ways

Tried in order, all registry-driven:

  1. It names a binary the registry lists. The original signal.
  2. Its command names an inference host. A job curling api.anthropic.com on a timer is reaching a model whatever binary it runs. inference_domains only, never domains — a scheduled installer download is an acquisition, and matching any domain would put every one of them on a page about what runs unattended.
  3. Its definition hands a script a model credential. This closes the blind spot 0.23.0 could only state. A job running nightly-report.sh names nothing matchable, but a launchd EnvironmentVariables or systemd Environment= setting ANTHROPIC_API_KEY says what it reaches.

Only variables the registry names for a tool. A generic API_KEY would have fired on every scheduled backup in the estate — a worse false positive than the one this whole sequence started with.

Two limits, stated rather than left to be discovered. A Windows scheduled task declares no environment — an action carries a command and inherits the user's — so the credential path cannot exist there, and a wrapper script on Windows stays invisible; the page says so. And EnvironmentFile= is not opened: it points at a file of secrets, and reading the names would mean reading the values.

substrate.office.com suppresses on the domain

For any non-browser, regardless of process. This deletes a maintained list rather than growing one: Microsoft can ship another shell process and it stays quiet without an entry.

The cross-signal join is written down, not built

docs/agentic.md now carries the three reasons it is not sound yet: the join key is a process name (python3 joins against every other python3), it needs inventory rather than findings or it recreates the flood this sequence removed, and an ambiguous join has no honest rendering on a page made of four filled boxes. What the cheap signals miss on a real estate is the specification for it.

Checked

Verified on a real machine and in a container, five job shapes:

runs claude                          -> reported (binary)
curls api.anthropic.com              -> reported (inference host)
runs nightly.sh + ANTHROPIC_API_KEY  -> reported (credential)
curls cursor.com/install.sh          -> silent
runs backup.sh + AWS_SECRET_ACCESS_KEY -> silent

One job produces one finding: several tools name ANTHROPIC_API_KEY, so the first match wins and the evidence names the variable rather than guessing louder. I caught that as a duplicate during testing, before it shipped.

Suites: registry 23, portal 615, scanner 210, receiver 290. Plus collector parity, build.py --check, both byte-identical invariants, shellcheck, PSScriptAnalyzer, and the containerised Linux collector harness (14/14).

docs/agentic.md updated so the documented signals match the code.