Security review
On this page
Every publish runs an automated security review before the registry accepts the version. If any check returns a blocking finding, the publish fails with 422 and the list of findings, and nothing is stored.
The checks
# | Check | What it catches | On failure |
|---|---|---|---|
1 |
|
| blocking; later checks do not run |
2 |
| signature does not match an active certificate of your vendor | blocking |
3 |
| no skills, no inbound events and no peers: nothing to install | blocking |
4 |
| an unknown entitlement, or | blocking |
5 |
| literal | blocking |
6 |
| a credential | blocking |
7 |
| an outbound method with no rate limit | warning on |
8 |
| a skill routed through an OPVS proxy with an unknown proxy path | blocking |
9 |
| an | blocking if rejected; warning while pending |
10 |
|
| blocking |
11 |
| no | warning |
12 |
| profession-only rules (see Profession anatomy) | blocking |
What runs when. At publish, the registry reviews your manifest and signature. The checks that read files inside the bundle (blocklist, rate_limits on your schemas, required_docs, the profession file checks) run when the bundle itself is reviewed. Run them yourself with the preflight, and treat them as required: they are what a full review checks.
What each check does
manifest_schema validates package.yaml against the rules on The package manifest. Code: manifest.schema, with the failing field path.
signature_verify finds your vendor's active certificate and checks the signature against the bundle hash. Codes: signature.unknown_vendor, signature.revoked, signature.mismatch.
meta_coherence refuses a package with skills: [], inbound_events: [] and peer_dependencies: []. Code: meta.empty_useless. Peers alone are fine (a meta-package).
entitlement_lint checks every entitlement is known: a fixed name, network.egress.provider.<name>, credits.consume.<name> or vendor.<vendor>.<surface>.<verb>. It also refuses network.egress.public when all your outbound URLs share one host. Codes: entitlements.unknown, entitlements.too_broad.
blocklist scans text files in the bundle for shell(, eval( and exec(. exec( is allowed only if the package declares an exec.* entitlement.
auth_scheme allows the credential types pat, api_key, oauth, oauth_pkce and hmac_sha256. Code: credentials.auth_type_unknown.
rate_limits checks that every method with an http line has a rate_limit, on the method or as a package default in provider_config.rate_limit. Code: rate_limit.missing.
base_url_lint applies to vendors whose skills run through an OPVS proxy: the skill's base_url must be one of the proxy paths OPVS gave you. Code: base_url.unknown_proxy_prefix.
network_egress applies when your manifest declares provider_config.upstream. Every host (the upstream and any absolute host in your method URLs) must be approved for your vendor. A new host files an approval request and gives a warning; calls to it are refused until OPVS approves it. A rejected host blocks. Codes: network_egress.host_pending_approval, network_egress.host_rejected.
required_docs requires README.md, SKILL.md and CHANGELOG.md at the bundle root. For the future verified tier, LEARNINGS.md and a non-empty tests/ folder are also expected (warnings today).
aliases_present warns when aliases is empty. Code: discovery.no_aliases.
The score
start 100
each blocking −25
each warning −5 (floor 0)The publish is refused by any blocking finding, whatever the score. A package with only warnings passes with a lower score. The score is stored on the version and shown with the package.
Reading a refusal
{
"error": "security_review_failed",
"score": 75,
"findings": [
{
"severity": "blocking",
"code": "credentials.auth_type_unknown",
"message": "credential 'ACME_KEY' uses auth_type 'device_code' which is not in the marketplace-approved set",
"path": "credentials.required[0].auth_type"
}
]
}Read code first: it names the check. message names the field and value. Fix it, bump the version, and publish again.
Common fixes
Code | Cause | Fix |
|---|---|---|
| unknown key, bad semver, missing | follow The package manifest |
| wrong key, or the certificate was rotated | sign with the key of your active certificate |
| the certificate was revoked | ask for a new certificate and re-sign |
| nothing to install | add a skill, or peers for a meta-package |
| a typo or a made-up name | use a known name or |
|
| use |
| a reserved credential type | use |
| a method with no rate limit | add |
| a new upstream host | ask vendors@opvs.ai to approve it |
| no aliases | add the phrases agents would search for |
Running the full review without publishing
The standalone review endpoint (POST /api/v1/security/review on the marketplace) runs every check, including the bundle checks, without storing anything. It accepts only OPVS's platform key today, so ask vendors@opvs.ai to run it on a candidate bundle.