Polyglot Manifest License and End-of-Life Gate
Pricing
from $20.00 / 1,000 manifest set run starts
Polyglot Manifest License and End-of-Life Gate
Read manifest files from more than one ecosystem. Report the license, the deprecation flag and the abandonment age of each package in one table.
Pricing
from $20.00 / 1,000 manifest set run starts
Rating
0.0
(0)
Developer
kingii98
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Read manifest files from more than one ecosystem. Report the license, the deprecation flag and the abandonment age of each package in one table.
A team that runs Node, Python, Go and Rust must usually run one license tool for each ecosystem, and must then join the results by hand. This Actor reads all four manifest kinds in one run and writes one flat table, plus one run summary with a pass or fail gate verdict.
Who this is for
An engineering lead or a legal reviewer at a small polyglot team. This person must approve a release, or must answer a due-diligence question, and has no commercial software composition analysis platform.
Run it once for each release tag, once each month as a compliance sweep, or once before a due-diligence hand-over. The manifest set stays the same, so the run repeats with no new setup.
What the Actor does
- Reads 1 to 25 manifest files. Give each one as pasted text or as a public URL.
- Parses each manifest and finds the packages it names.
- Resolves each package against its public registry: npm, PyPI, crates.io or the Go module proxy with deps.dev.
- Writes one dataset row for each package in each manifest, and one run summary record.
- Stores a baseline document for the manifest set, so the next run can name what changed.
The Actor makes HTTP GET calls only. It does not clone a repository, install a package, or run any manifest content. It uses no browser, no proxy, no language model and no paid API.
Supported manifest types
| Manifest type | Ecosystem | Registry |
|---|---|---|
package.json | npm | registry.npmjs.org |
package-lock.json | npm | registry.npmjs.org |
requirements.txt | PyPI | pypi.org |
go.mod | Go | proxy.golang.org and api.deps.dev |
Cargo.toml | crates.io | crates.io |
The manifest type comes from the file name, so services/api/go.mod is a
go.mod. You can also state the type field when the file name does not say.
Input
| Field | Type | Default | Meaning |
|---|---|---|---|
manifests | array | four-ecosystem sample set | 1 to 25 manifests. Each item takes name, an optional type, and either content (pasted text) or url (a public URL). |
licensePolicy | array of strings | ["GPL-3.0-only", "AGPL-3.0-only"] | SPDX identifiers the report must flag. |
abandonmentDays | integer | 730 | A package with no new release in this many days gets the abandoned flag. |
includeTransitive | boolean | false | Resolve one level of transitive dependencies. |
failOnDeprecated | boolean | true | A deprecated package counts as a policy failure. |
failOnAbandoned | boolean | false | An abandoned package counts as a policy failure. |
failOnUnknownLicense | boolean | false | A license the Actor cannot read as SPDX counts as a policy failure. |
baselineKey | string | "" | Key-value store key for the baseline. Empty means the Actor derives one key from the manifest names. |
Every field has a default, so a run with empty input {} succeeds and reports
the sample manifest set.
A manifest given as a URL
{"manifests": [{ "name": "go.mod", "url": "https://raw.githubusercontent.com/owner/repo/main/go.mod" }]}
A URL must be public. The Actor refuses loopback, private and reserved addresses, refuses credentials in the URL, checks every redirect hop against the same rule, allows at most 3 redirects, and reads at most 2 MB for each manifest.
Output
Package rows
One row for each package in each manifest.
| Field | Meaning |
|---|---|
recordType | Always package. |
manifestName | The manifest the package came from. |
ecosystem | npm, PyPI, Go or crates.io. |
packageName | The package name. |
dependencyKind | DIRECT or TRANSITIVE. |
requestedRange | The range the manifest states. |
resolvedVersion | The version the Actor reports on. |
latestVersion | The newest version the registry lists. |
spdxLicense | The SPDX expression, or UNKNOWN. |
licenseIdentifiers | The SPDX identifiers in the expression. |
licensePolicyVerdict | PASS, FAIL or UNKNOWN. |
deniedLicenseIdentifiers | The identifiers your policy denies. |
deprecated | True when the registry marks the release deprecated. |
deprecationMessage | The message the registry gives. |
lastPublishDate | The newest release date of the package. |
daysSinceLastPublish | Days from that date to the run. |
abandoned | True when daysSinceLastPublish reaches abandonmentDays. |
repositoryUrl | The source repository the registry states. |
repositoryArchived | Always null in version 1. See the limitations. |
repositoryArchivedStatus | Always NOT_CHECKED in version 1. |
resolutionStatus | RESOLVED, NOT_FOUND or ERROR. |
resolutionError | The reason the package did not resolve. |
policyFailure | True when the row breaks the policy. |
policyFailureReasons | One line for each reason. |
baselineStatus | BASELINE_CREATED, NEW, CHANGED or UNCHANGED. |
changesSinceBaseline | Field, previous value and current value for each change. |
Run summary
One record with recordType set to run-summary. It carries gateVerdict
(PASS or FAIL), packageCountByEcosystem, policyFailureCount,
deprecatedCount, abandonedCount, unresolvedPackageCount,
resolutionRate, manifestErrors, the baseline counts
(baselineFirstRun, changedPackageCount, newPackageCount,
removedPackageCount), and truncated with packageLimit.
The gate verdict is FAIL when at least one row is a policy failure.
truncated is true when the manifest set holds more packages than
packageLimit, and the table therefore shows only the first packageLimit
packages. Read it before you treat the table as a complete report.
The baseline
The Actor keeps one baseline document for each manifest set in the named
key-value store pkg-eol-gate-baselines. The baseline holds the resolved
version, the license, the license verdict, the deprecation flag and the
abandonment flag of each package. One baseline slot is one package inside one
manifest (ecosystem|package|manifest), because two manifests can request
different ranges of the same package and therefore hold different values.
- The first run writes the baseline and reports the full table. Every row gets
BASELINE_CREATEDand reports no change. - A later run compares each row against the baseline and marks it
NEW,CHANGEDorUNCHANGED. A changed row names each field that moved.
The default store cannot hold the baseline, because Apify purges it when a run starts.
The run always succeeds
A failed gate, an unreachable manifest URL, a package the registry does not
know, and an empty manifest are all report results, not malfunctions. The run
ends with status SUCCEEDED and carries the verdict in the status message and
in the run-summary record. Read gateVerdict to gate a release.
Invalid input is reported the same way: the Actor writes one record with
gateVerdict set to ERROR and an inputError message, and the run
succeeds.
Pricing: pay per event
| Event | Unit | Counted as |
|---|---|---|
manifest-set-run-start | one run | Charged once, at the start of the run. |
package-resolved | one unique package resolved against a registry | One charge for each unique (ecosystem, package name) pair sent to a registry. A package that two manifests name is charged once, also when the two manifests request different ranges of it. A lookup that ends NOT_FOUND or ERROR was still sent to a registry, so it is charged. |
policy-failure-recorded | one policy failure written to the report | One charge for each failing row in the dataset. A failing package that two manifests name writes two rows, so it is charged twice. |
Bounds
| Bound | Value |
|---|---|
| Manifests for each run | 25 |
| Characters for each pasted manifest | 2 000 000 |
| Bytes for each fetched manifest | 2 000 000 |
| Packages for each run | 1 500 |
| Redirects for each manifest URL | 3 |
| Registry requests in parallel | 6 |
| Timeout for each request | 25 s |
| Bytes for each registry response | 8 000 000 |
| Denied SPDX identifiers | 100 |
Version 1 limitations
- Version resolution is a documented rule, not a solver. Each manifest is resolved against the range that it states, so two manifests that request different majors of one package get their own row, version and license. An exact version in the manifest wins. A caret or tilde range takes the highest registry release inside the range. Anything else takes the highest stable release. This is not what a real package manager resolves in every case. For an exact answer, give a lock file.
- The repository archived flag is not read. Version 1 makes no call to a
source forge. The GitHub API does give
archivedwithout a token, but only at 60 requests each hour for each address, which a shared Actor cannot hold to. The column is present and holdsnull, withrepositoryArchivedStatusset toNOT_CHECKED. - PyPI and crates.io have no deprecation flag. A yanked release is the
nearest equivalent, and it carries the same instruction: do not use this
release. The Actor reports a yanked release as deprecated and says so in
deprecationMessage. - Transitive resolution covers npm and PyPI only. Those two registries
state a package's dependencies in the same document the Actor already reads.
crates.io does not, so a Cargo transitive level would need one more call for
each crate. A Go
go.modalready lists its indirect requirements, and the Actor keeps them whenincludeTransitiveis on. - A license the registry does not state as SPDX is reported as
UNKNOWN, not guessed. Turn onfailOnUnknownLicensewhen an unread license must stop a release. - The abandonment age measures the package, not the pinned release. It uses the newest release date of the whole package, which is the right measure for "is this project still maintained".
Local development
uv syncuv run pytestuv run ruff check .


