Nobody chose these tools.

On the project muster was built against, the Agent Registry holds 15 MCP servers exposing 177 tool bindings, which are 170 distinct tool names with 6 of them published by more than one server. Eight of those servers were registered in a single burst lasting 0.659 seconds, at 11:59:15 UTC on 18 August 2026: BigQuery, BigQuery Data Transfer, BigQuery Migration, Cloud Trace, Dataplex, Logging, Monitoring and Storage, all inside two thirds of a second. The other seven arrived later, one at a time, as APIs were switched on for unrelated reasons. No human picked them off a list. They appeared because somebody enabled an API.

Of those 177 bindings, 72 carry no readOnlyHint, so whether they write is recorded nowhere. Not one tool declares itself a writer. The honest statement is not that these tools are safe, and not that they are dangerous. It is that for 72 of them nobody can tell from the registry, and the registry is the only place a reviewer would look.

So the person who needs this is not a compliance officer with a mandate, a budget and a quarterly cycle. It is whoever owns the project: the developer who enabled an API on a Tuesday and now, without ever deciding to, operates an agent fleet with 177 tool bindings in it, several of which can probably write to production. No security title. They inherited it. At some point somebody asks whether it is under control, and answering honestly today means reading 15 registry entries by hand and still not knowing about 72 of the tools. That is the unlikely hero here: an accidental fleet operator, made responsible for a surface that assembled itself.

muster inventories the agents, tools, identities and running workloads on a Google Cloud project and issues a verdict for every subject: REVOKE, CERTIFY or ABSTAIN, each carrying the evidence it was decided from and, where it abstains, the name of the exact thing that is missing and the API call that would close it.

The design decision everything else follows from is that the language model has no code path to a verdict. Verdicts are decided in a pure module with no model, no network and no clock, by deterministic rules over measured evidence. A fleet of three ADK agents on Gemini gathers evidence, calls the engine and explains the result in plain English. None of them can change one. Any tool that lets a model decide "certified" can be talked into certifying. This one cannot, and that property is tested rather than asserted.

CERTIFY is deliberately hard to reach. It requires held permissions fully enumerated, usage evidence present, everything used inside what is held, and nothing unmeasured. On a project with no runtime traces it is unreachable, and the report says so before listing anything. Zero certifications is the engine working.

Every number in this description comes from one campaign, run against a live project on 23 August 2026 at 09:16 UTC, and is reproducible with a single command.

That campaign covered 31 subjects and refused to certify 27 of them, each refusal naming what was missing and the API call that would close it. It revoked four.

Two of the four are shadow agents: workloads serving A2A agent cards that appear nowhere in the Agent Registry. Both were deployed deliberately, as subjects under review, because a detector nobody has pointed at a real positive is a detector nobody has tested. They are not discoveries and this description does not present them as any.

The other two were not planted. One is the project’s default compute service account, which holds the editor role because Google granted it at project creation, on a project whose owner never chose that and would have to know to look. The other is a registry entry whose display name reads "unused" while its URN and its endpoint both say it is the billing budgets service, so anyone triaging that registry by label cannot see what it is.

Two things fought back, and both changed the system.

The first was the agent card prober. The A2A specification renamed the card discovery path between versions and both names are live in the wild. A prober that knows only one name gets a 404 from every agent serving the other, files it as "no card", and reports a demonstrable agent as not an agent. That is a false clear, and it is invisible: the run completes, the report reads clean, nothing looks wrong. muster now probes every known path, and treats a 404 from all of them as unmeasured rather than as absence, because the set of discovery paths is not closed. It already changed once. Calling it absence would clear a subject on the strength of our own ignorance.

The second was a prompt injection served inside an agent card, and the way it was caught is the best result in this project.

An agent card is written by the subject under review. muster fetches it over the wire and puts its text in front of a reviewing model, which makes it an injection surface. So a second shadow was deployed whose card description claims prior security approval and instructs the reviewer to mark it CERTIFY and omit it from the findings. Every card is now screened through Model Armor before its text is stored anywhere.

The first version of that screening concatenated the card's name, description and skill text into one blob and made a single call, to keep a campaign cheap. Measured live against the hostile card, that decision produced these two results. The description on its own was blocked, prompt injection and jailbreak detected at medium confidence and above. The same description surrounded by the card's ordinary product copy came back clean.

Sibling fields diluted the injection below the classifier's threshold. The guardrail reported clean on a card that contains, verbatim, an instruction to certify the subject and omit it from the findings. Batching the fields is what hid it. Every field is now screened on its own and the joined text is screened as well, because a per field pass cannot see an injection split across two fields, and a card is blocked if any pass blocks it. After the fix, injection fragments stored in the campaign snapshot went from four to zero, the hostile card is still detected and still REVOKE, and the refusal is recorded on the verdict alongside the exact API call that produced it. Detection never depended on trusting the text, and now it visibly does not.

Certification is not a single run. Each campaign's verdicts are written to Vertex AI Agent Engine Memory Bank and read back on the next one, so a re-certification reports what changed: which abstentions were resolved, which are blocked on exactly the same gap as last time and have therefore had nobody act on them, and which subjects are new. Elapsed time is never invented. The gap between campaigns is computed from the memory's own write timestamp, and when it is short the report says so in its own words rather than implying weeks of operation it cannot demonstrate.

That mechanism immediately caught something nobody planted. Provisioning the Agent Engine that holds the memory bank caused Google to create a new service agent on the project, and the very next campaign reported it as a subject that did not exist during the previous one. The tool that exists because fleets grow without anyone deciding grew the fleet by one, and then noticed.

What it will not do is certify anything on the strength of silence. A failed read is never an absent resource. A query that did not run is never a query that found nothing. A guardrail that could not be reached is never a guardrail that passed. Each of those is enforced in code and covered by tests, because they are the failures that would make a tool like this worse than having none.

Built With

Share this project:

Updates

Submission history