Verune

Verune API

The API answers questions about AI agent assets (MCP servers, plugins, skills) from Verune's continuously crawled corpus. Verune never runs in your traffic path and never blocks anything. Decisions are advice that your own gateway, proxy, installer or CI job enforces.

Authentication

Create a key on your API keys page (Pro and Team plans, workspace owner only). Send it on every request:

Authorization: Bearer vrn_...

Keys are shown once and stored only as a hash. A key stops working when it is revoked, or when its creator leaves the workspace.

Errors and limits

Errors use RFC 9457 problem JSON with a stable type. Validation errors list {"path","code"} pairs and never repeat your input.

Searches, asset reads and lookups count against your monthly search allowance (one per lookup subject). A report counts once per asset per month. Request bodies are limited to 256 KiB.

Read endpoints (Pro and Team)

GET  /api/v1/assets?q=filesystem&limit=10
GET  /api/v1/assets/{assetId}
GET  /api/v1/assets/{assetId}/report
POST /api/v1/lookup        {"subjects": [ {subject}, ... up to 100 ]}

Lookup returns, per subject in request order, how it resolved and the current risk level:

{"items":[{"subjectKey":"npm:@acme/mcp-files@2.1.0",
  "resolution":{"state":"RESOLVED","assetId":"…","revisionId":"…","versionSpecified":true,"candidateCount":1},
  "asset":{…public summary…},"riskLevel":"MEDIUM"}]}

Subjects

A subject names one asset. Exactly one of these shapes:

{"kind":"npm","name":"@acme/mcp-files","version":"2.1.0"}     version optional
{"kind":"pypi","name":"acme-mcp","version":"0.4.0"}           version optional
{"kind":"mcp_registry","name":"io.github.acme/files","version":"1.0.0"}
{"kind":"repository","url":"https://github.com/acme/mcp-files"}
{"kind":"sha256","value":"<64 hex characters of the artifact>"}
{"kind":"remote_endpoint","url":"https://mcp.example.com/sse"}
{"kind":"asset","assetId":"…","revisionId":"…"}             revisionId optional

Resolution states: RESOLVED, UNKNOWN (not in the corpus), AMBIGUOUS (several assets match, for example a monorepo), UNKNOWN_VERSION (the asset is known but that version is not). A missing version means the latest known revision.

Team endpoints

GET    /api/v1/inventory?after={id}&limit=100
POST   /api/v1/inventory                    {"subject":{…},"label":"prod"}
DELETE /api/v1/inventory/{id}
POST   /api/v1/inventory/{id}/approve       {"revisionId":"…"}
DELETE /api/v1/inventory/{id}/approve
GET    /api/v1/alerts?after={seq}&limit=100
GET    /api/v1/policy
PUT    /api/v1/policy                       {policy document}
POST   /api/v1/decisions                    {"subject":{…},"context":{"client":"ci-gate"}}
GET | PUT | DELETE /api/v1/webhook          PUT body: {"url":"https://…"}
POST   /api/v1/webhook/rotate-secret

Register an unversioned subject to follow upstream releases, or a versioned one to watch a pinned install. Verune re-checks the inventory every 15 minutes and records alerts: NEW_REVISION, RISK_LEVEL_CHANGED, FINDINGS_CHANGED, UPSTREAM_REMOVED, RESOLUTION_CHANGED, DRIFT_FROM_APPROVED. Read them with GET /api/v1/alerts, passing the last seq you processed as after.

Policy document

Every key is required. Each PUT creates a new immutable version; until you store one, this default applies as version 0:

{"schema":"verune-policy/v1",
 "onUnknownAsset":"WARN","onAmbiguous":"WARN","onUnknownVersion":"WARN",
 "onUnscored":"WARN","onRemovedUpstream":"DENY",
 "denyAtOrAbove":"CRITICAL","warnAtOrAbove":"HIGH",
 "denyFindingTypes":[],"warnFindingTypes":[],
 "inventory":{"mode":"OFF","onDriftFromApproved":"WARN"},
 "allow":[],"deny":[]}

Decisions

{"decisionId":"…","decision":"WARN",
 "reasons":[{"code":"RISK_LEVEL","action":"WARN","detail":"HIGH"},
            {"code":"VERSION_UNSPECIFIED","action":"ALLOW","detail":null}],
 "subject":{…},"subjectKey":"npm:@acme/mcp-files@*",
 "asset":{"assetId":"…","name":"…","kind":"MCP_SERVER"},
 "revision":{"revisionId":"…","version":"2.1.0","contentHash":"…"},
 "risk":{"level":"HIGH","modelVersion":"v5","assessedAt":"…"},
 "policyVersion":3,"evaluatedAt":"…","dataAsOf":"…","cacheTtlSeconds":300,"advisory":true}

The decision is the most severe action among the reasons. Reason codes: EXPLICIT_DENY, EXPLICIT_ALLOW, UNKNOWN_ASSET, AMBIGUOUS_SUBJECT, UNKNOWN_VERSION, REMOVED_UPSTREAM, UNSCORED, RISK_LEVEL, FINDING_TYPE, NOT_IN_INVENTORY, DRIFT_FROM_APPROVED, VERSION_UNSPECIFIED.

Caching. Honor Cache-Control: private, max-age: 300 seconds for resolved subjects, 60 otherwise. Cache by subject key.

Failure handling. If the API is unreachable or returns 5xx, Verune gives no decision. We recommend failing open with a warning in your client, so a Verune outage never blocks your own users. Fail closed only where your risk tolerance requires it.

Risk comes from automated static analysis of published artifacts under the active rule model. It is not human-reviewed and artifacts are never executed.

Webhooks

PUT /api/v1/webhook with an https URL on port 443. The response to the first PUT (and to rotate-secret) contains the signing secret once. Verune posts batches of up to 50 alerts:

POST {your url}
Content-Type: application/json
Verune-Delivery: <uuid>
Verune-Signature: t=1760000000,v1=<hex>

{"workspaceId":"…","alerts":[{alert}, …]}

To verify: compute HMAC-SHA256 with the secret over t + "." + raw body, compare it to v1 in constant time, and reject timestamps older than five minutes. Reply with any 2xx to acknowledge. Failures retry with exponential backoff up to an hour; after 20 consecutive failures the webhook is disabled until you PUT it again. Alerts are never lost: GET /api/v1/alerts always has them.