Repository navigation
Releases: ciceroyang/agentgate
Release list
0.6.0
AgentGate 0.6.0 fixes evidence-gate correctness and tool-identity handling, adds bounded Codex MCP configuration discovery, and verifies the actual package before publishing it.
Important fixes
- Required evidence that is absent or incomplete no longer passes admission. An explicitly missing, malformed or sample index is an input error; valid, exact, complete evidence can still pass.
- Adding required scanners cannot remove the overall completeness check.
- Discovery preserves distinct tools behind the same alias and retains Python package identity through inventory export.
- Watch detects changed findings and severity even when the package version and finding count stay the same.
- Supported Codex MCP TOML tables are read without starting configured tools. Disabled entries remain in diagnostics but are omitted from inventory exports; unsupported shapes remain incomplete.
- Collection caches bind observations to source, package and scanner identity. Evidence packs require actual decision records and nonempty archives for the corresponding evidence classes.
- Release manifests must agree; the npm workflow publishes the exact tarball that passed delivery acceptance, with a final integrity check before publishing.
Upgrade notes
This is a 0.x minor release with intentional behavior changes. Read the 0.6.0 upgrade guide.
- Use
discover --format inventoryfor stable JSON input. Default discovery output is registry-qualified text only when all exported package identities are known; alias-only entries make it JSON. - Treat incomplete evidence as incomplete, not as a pass. Do not start new required-evidence gates on the affected 0.5.0 package.
- Preserve existing watch archives. The first capture after upgrading an older projection may establish a new identity baseline; it is not automatically a newly discovered vulnerability.
- A source or package upgrade does not refresh the deployed index or migrate old collection caches by itself.
Install the exact version
npx --yes --ignore-scripts @zhiliangtech/agentgate@0.6.0 versionVerification and release assets
- Local release validation: 683 tests, 681 passed, no failures, and two Caddy-dependent tests skipped on the local machine. GitHub CI passed its standard, minimum-Node and Docker jobs.
- All nine synthetic delivery scenarios passed against the CI-built package, the tarball downloaded from the public npm registry, and a fresh anonymous registry installation with install scripts disabled and an isolated cache.
- The public tarball is byte-for-byte identical to the CI artifact. Its provenance identifies the reviewed source commit and the successful publishing run.
npm audit signaturesverified both the registry signature and the provenance attestation for the installed package.- Attached: the exact npm tarball, CI delivery receipt, CI acceptance results, public-package verification, shareable extracted/installed acceptance results, default-version verification, and the CI-built CycloneDX SBOM.
Public tarball SHA-256:
c88fa7355952686de86f5ad453a4ef96d1d9b216e008a07d9252283c48db4a2b
The account-authenticated promotion is complete: both latest and next point to 0.6.0. Anonymous installations without a version selector and with explicit @latest, each using a fresh directory and cache, both returned agentgate 0.6.0. See the attached latest-verification.json. Pin the version in automation for reproducibility.
These checks are scoped regression and delivery evidence, not customer acceptance, a security certification, or proof that any hosted deployment has been upgraded.
0.5.0
Release 0.5.0 — the package audit reaches the published index, and the gaps get names.
Attached: our own CycloneDX 1.5 SBOM, generated by the publish workflow from this commit
before the tarball was uploaded. It covers every shipped source file with a sha256, and records
that this package has no third-party runtime dependencies.
The npm tarball is built from the same commit by GitHub Actions and carries SLSA provenance:
npm view @zhiliangtech/agentgate@0.5.0 dist.attestations
npm view @zhiliangtech/agentgate@0.5.0 dist.integrity
What changed
refresh never passed --audit to build-index. Every scheduled rebuild therefore dropped the
package audit and left thousands of audited packages described as "a package is declared here" —
no error, no log line, the artifact on disk the whole time. With it joined, repository records
carry a packageManifest component and /v1/index/summary names what could not be measured
instead of folding it into one word.
Three vocabularies became finer, in the same direction: a difference that was being averaged away
is now two facts. Metadata that could not be read says whether the registry has no such package
or this run could not reach it. A hook script says whether the package does not ship it, a CDN
did not answer, or this step did not ask. And a declared install hook pointing at a file the
package does not contain became its own finding, install-hook-script-missing-from-package,
rather than an unknown nobody could sign off on.
The four MCP tools declare outputSchema, and each description says when to use it and which
sibling answers the other case.
Full notes: CHANGELOG.md, and
what an existing installation has to do:
docs/operations/upgrade.md.
Full changelog entry
-
A file the published package does not ship is now a finding instead of a gap.
registryProvenance
marked the evidence incomplete whenever a hook script could not be read, so
@yagyeshvyas/vibeguard— whose manifest declares"postinstall": "node scripts/postinstall.js"
while the tarball contains no such file — was recorded asinstall-hook-script-unavailable
(unknown) withcomplete: false. We had asked both unpkg and jsdelivr and both answered. A
checked absence is knowledge, the same way a repository tree with no manifest in it is; the rule
now says so, and the finding is what is actually true:install-hook-script-missing-from-package
(high), with evidence that is complete. A CDN that never answered, or a ref this step chose not
to fetch, still leaves the evidence incomplete — those are worth retrying, and this one is not. -
A hook script we could not read now says which of three things happened.
fetchHookScript
returnednullfor a file the package does not ship, for a CDN that never answered, and for a
path this step refused to request — one word,install-hook-script-unavailable, for all three.
The first is knowledge about the package; the second is knowledge about this run and is worth
retrying; the third is this build's own choice, and refs past the five-per-package cap now say
hook-script-not-fetchedrather than borrowing the word for a missing file. The reason travels
into the finding's evidence and into the provenance digest, so a recorded review covers it. -
The index carries the audit's own reason for a package it could not read, instead of one word for
both cases.auditReasonmapped everymetadata-unavailabletopackage-metadata-unavailable,
so the published coverage summary still merged "the registry has no such package" with "this run
could not reach the registry" — the two facts the audit had just started telling apart. An audit
written before it recorded a reason still gets the old word rather than silence. -
agentgate refreshhands--audittobuild-index. It did not, so every scheduled run rebuilt
the published index without the package audit and the repository records fell back to "a package
is declared here" — 6,199 audited packages' worth of evidence, dropped on a four-hourly timer with
no error and no log line. The artifact was on disk the whole time. This is the quiet version of
the mistake the rest of this section is about: not a wrong claim, but a capability that exists in
the code and never reaches the file anyone reads. -
A package we could not get metadata for now says which of two different things happened.
fetchNpmDocumentandfetchPypiDocumentreturnednullfor every failure, so a 404
("the registry does not have this package") and a timeout or a 429 ("this run could not reach
the registry") arrived at the record as the samemetadata-unavailablewith no reason at all.
The first is a fact about the package; the second is a fact about the run. Merging them meant the
published file could not be used to tell a wrong coordinate from a failed fetch — the same shape
of mistake asno-package-declared-in-repositorybelow, in a field with a wider blast radius.
The fetch functions keep their existing signatures and now have…Outcometwins that carry a
reason (package-not-found,registry-unreachable,registry-http-<status>,
metadata-not-json,package-name-not-requested), and the audit writes it down. The last one
is deliberately not called "invalid name": npm still serves legacy packages that predate the
lowercase rule (JSONStreamandBase64answer 200 today), soisNpmPackageNamedescribes what
this step will ask about, not what the registry has. -
The repository record no longer claims something it never checked. Its
packageManifest
component isskippedwith reasonpackage-not-inspected(was
no-package-declared-in-repository). The old string read as a finding about the repository —
"there is no package declared here" — when this build simply never looks: repository records are
built from the census and the classification, and neither carries package data. Three
repositories that do declare apackage.json(firecrawl/firecrawl-mcp-server, upstash/context7,
apify/apify-mcp-server) were recorded as declaring none. The reason now names what this build
did, and nothing about the repository. -
The classification step now keeps the manifest path instead of only using it to pick a kind.
classifyPathsalready matchedpackage.json,pyproject.toml,Cargo.toml,go.modand the
rest — a repository with a server fileanda manifest is whatserver-likemeans — and then
discarded the path. It costs nothing to keep: the file tree was already fetched and parsed. With
it, the reason a package was not read has three honest states rather than one:
manifest-found-not-inspected(the tree lists a manifest; this step does not read it),
no-package-manifest-in-repository(the tree was read and holds none — a checked absence), and
package-not-inspected(a classification written before the field existed, which proves nothing
either way). None of the three can make a recordclean. -
packages/collect/scripts/fetch-manifests.mjsreads the manifest a repository declared and
extracts the coordinates a package audit needs — registry, name, version — frompackage.json,
pyproject.toml,Cargo.toml,composer.json,pom.xml,*.csprojandgo.mod. It reads
fromraw.githubusercontent.com, which does not spend GitHub API quota, and it writes down the
URL and a sha256 of the bytes it read, so anyone can fetch the same file and check the digest. It
does not derive a name from the repository name and does not invent a version: a manifest without
one yieldsversion: null, which the policy layer already treats as missing evidence. A manifest
it cannot read is reported asunreadablewith the HTTP status, and one it can read but cannot
parse is reported with the parse error — neither throws. -
packages/collect/scripts/audit-packages.mjstakes those coordinates and runs the same audit the
registry path runs, so a repository record can carry measured evidence instead of a component that
is skipped because nobody looked. The server object it builds carries notransport: inventing
stdiowould add a finding about a transport nobody checked. Hook scripts are fetched so their
status is real rather than "missing because we did not ask". A registry this step cannot audit yet
is reported asunsupported-registry, and a manifest with no package name as
no-package-name-in-manifest— both are facts, and neither is a pass. -
build-index.mjs --audit <package-audit.json>joins that audit back into the index, so a
repository record can carry the package coordinate and a measuredpackageManifestblock
instead of an emptypackagesarray. Without the flag the records are exactly what they were.The invariant holds, and it is now spelled out. A repository record still can never be
clean,
because a third required component says why:repositorySource, skipped with reason
source-not-read. The metadata block reads a repository's public metadata; the package block
reads the package it publishes; neither reads the repository itself, which is what a reader
would have to inspect to say anything about the server. Before this, "never clean" rested on the
package half being skipped wholesale. It now rests on the gap that actually remains — and when
that gap is closed, the component is removed rather than quietly reinterpreted. -
A second mapping:
agentgate framework --id eu-aiacovers the record-keeping obligations of
Regulation (EU) 2024/1689 — Article 12 and Article 19. Eight items, and the count is published
with them: 2 ours, 4 the customer's, 2 a third party's. Almost nothing is ours, and that is the
honest result rather than a modest one — we do not run the customer's AI system and we do...
0.4.0
Release 0.4.0 — the index can carry two kinds of record, counted apart.
Attached: our own CycloneDX 1.5 SBOM, generated by the publish workflow from commit
682b3bd before the tarball was uploaded. It covers the 138 shipped source files with a
sha256 each, and records that this package has no third-party runtime dependencies.
The npm tarball is built from the same commit by GitHub Actions and carries SLSA provenance:
npm view @zhiliangtech/agentgate@0.4.0 dist.attestations
npm view @zhiliangtech/agentgate@0.4.0 dist.integrity
-
The index can carry two kinds of record, and they are counted apart.
build-index.mjs --github <census> --classification <file>adds a repository-level record for every repository the
classification called a server and the registry does not already cover. Such a record can
never beclean: itspackageManifestcomponent isskipped
(no-package-declared-in-repository), so the state is incomplete by construction. A repository
already represented by a registry row is not added again, and two records may not share one
identity./v1/index/summaryand the evidence page report registry entries and repository
records separately, because one coverage percentage over both would flatter the second kind. -
agentgate refresh --repositoriesruns the slower half of the chain: an incremental GitHub
census (--since, unioned with the previous output) followed by an incremental classification
that only fetches repositories whosepushed_atmoved, with a path kept for every verdict.
Without the flag, and without the artifacts on disk, the index is exactly what it was before. -
The formats are frozen and the promise is written down:
docs/spec/compatibility.md says what each version identifier means,
what may change inside one, how a breaking change is announced, and which versions are supported.
Every spec now declaresfrozenorprovisional, and
docs/operations/upgrade.md carries a section for every released
version, including the 0.1.1 change that made a stricter exit code look like nothing at all. -
SECURITY.md says how to report a vulnerability, what to expect, what is in scope,
and which of our own properties a reporter can check. Private vulnerability reporting on GitHub
is switched on. -
test/governance.test.mjsholds both promises to the code and to each other: the identifiers in
the policy are the ones the modules export, and the upgrade guide and the CHANGELOG must name the
same versions.
0.3.0
The deliverable a vendor hands to the person reviewing them.
agentgate packwrites one directory:pack.json,pack.html,answers.aicaiq.md, a per-file
manifest.txtand amanifest.sha256seal.agentgate pack --verify <dir>recomputes every hash.- Every answer we claim points at evidence in the same directory; everything we could not measure is
counted at the top of the page, and the command exits 2 rather than 0 when one of our answers is
only partly measured. - The AI-CAIQ mapping now classifies all 58 items of the four domains a reviewer asks a vendor about
(STA 19, CCC 11, LOG 21, A&A 7): 13 ours, 41 the customer's, 4 an independent assessor's. Nine
evidence classes decide what "measured" means, and a claim with no evidence class behind it fails
the test suite. - Example pack, generated from the live index and verifiable with
pack --verify:
docs/samples/evidence-pack-example.
17 new tests, 594 total, 592 pass, 2 skip. Published to npm as next; latest stays 0.2.4 until it is
promoted. The official MCP Registry has 0.3.0 under io.github.ciceroyang/agentgate.
Full notes: CHANGELOG.md
0.2.5
A patch with no runtime change: the links follow the apex.
- The product moved to https://xn--5kvo87g.com/ and the personal site that used to live there moved to https://cicero.xn--5kvo87g.com/.
- The old host
app.xn--5kvo87g.comredirects pages to the apex and keeps serving/v1,/healthand/badgedirectly. - README, site pages,
server.jsonandsecurity.txtnow point at the apex.
This release also exists so the official MCP Registry can take the new websiteUrl: it refuses to republish an existing version, so a metadata fix has to ride a patch.
Published to npm as next; latest stays 0.2.4 until it is promoted.
Full notes: CHANGELOG.md
0.2.4
The MCP server, and the registry metadata for it.
agentgate mcpserves the index to any MCP client over stdio: four read-only tools —lookup_server,inventory_tools,coverage_reportandcheck_project. It reads the local index and never writes, uploads or runs a scanned tool; an incomplete record is reported as incomplete, and a missing record as missing rather than safe.server.jsonandpackage.json'smcpNamedescribe the same server for the official MCP Registry, which checks that the npm package and its metadata agree.- The coverage counting moved into
packages/collect/src/coverage.mjs, so the CLI script and the MCP server report the same numbers.
Published to npm as next; latest stays 0.2.0 until it is promoted.
Full notes: CHANGELOG.md
0.2.3
Third attempt at the npm publish, and this one fixes the cause rather than the symptom.
actions/setup-node@v4 exports a placeholder NODE_AUTH_TOKEN; npm 11 preferred that placeholder over the OIDC trusted-publishing exchange, and the registry answered 404 Not Found - PUT. The workflow now uses actions/setup-node@v7 (which no longer exports the placeholder) and removes the generated .npmrc before publishing, so npm has no token at all and falls through to OIDC.
npm never received 0.2.1 or 0.2.2, so this release carries their changes:
- The Action is named Zhiliang agentgate for the GitHub Marketplace.
- The scan-execution coverage record and the inventory coverage report.
Published to npm as next; latest stays 0.2.0 until it is promoted.
Full notes: CHANGELOG.md
0.2.2
Fixes the npm publish: the workflow now clears NODE_AUTH_TOKEN for the publish step.
actions/setup-node writes an _authToken line into .npmrc and exports a placeholder token whenever registry-url is set. npm 11 preferred that placeholder over the OIDC trusted-publishing flow, and the registry answered 404 Not Found - PUT — which reads like a permissions problem and is not one.
0.2.1 was a GitHub release only; npm never received it. This release carries the same changes:
- The Action is named Zhiliang agentgate for the GitHub Marketplace.
- The scan-execution coverage record: every index record says which scanners ran, and a record with an unmeasured required component can never be
clean. - The inventory report shows, per tool, which scanners actually ran.
npm note: 0.2.2 did not reach npm either. Clearing the token exposed the second problem: the trusted-publisher binding was stale because the repository had been deleted and recreated, so the OIDC token exchange answered package not found. Deleting and re-adding the binding fixed it, and 0.2.3 is the version npm has.
Full notes: CHANGELOG.md
0.2.1
The Action is now named Zhiliang agentgate so it can be published to the GitHub Marketplace: the plain name is reserved by an existing account, and Marketplace requires the action name to be unique. uses: ciceroyang/agentgate@v0.2.1 is unchanged.
This release also ships the coverage work that has been on main since 0.2.0:
- Every index record carries a
scanExecutionblock: which scanners were required, which completed, and whether their output was present, readable and self-consistent. A record with a failed or unmeasured required component can never beclean. - The record is surfaced where it is read: SARIF
invocations[].executionSuccessful,/v1/index/summarycoverage counts,/v1/serversrows, and a policy'srequired.scanners. - The inventory report shows, per tool, which scanners actually ran; a record whose own coverage block says a required scanner did not finish cannot reach
matched. scripts/coverage-stats.mjsprints the coverage distribution and fails when a record claims a verdict its coverage block cannot support.
npm note: 0.2.1 did not reach npm. The publish pipeline had two problems in a row — a placeholder NODE_AUTH_TOKEN that npm preferred over OIDC, and a trusted-publisher binding left stale by the repository being deleted and recreated. Both are fixed, and 0.2.3 is the version npm has.
Full notes: CHANGELOG.md
0.2.0
Plainer README (English and Chinese), the four commands, and productionization P0–P6.
/metricsand an optional access log, the health check with mail alerts, zero-dependency enforcement with our own CycloneDX SBOM, request limits, backup with a restore drill, the release check, and the public security and privacy pages.
Use 0.2.0 or later. The provenance attestations of 0.1.0–0.1.2 name commits that are no longer on any branch or tag in this repository.
Full notes: CHANGELOG.md