Arvexa OS
Get API key
Loading API Reference from OpenAPI…
Guide

Plugin Security

The scan gate, secret hygiene, and webhook verification

Every install requires a PASSED security scan of the exact package bytes. The scan runs static rules over the archive, checks the signature status, and hands bounded content to a malware adapter. Verdicts are bound to one content hash: re-packing changes the hash and invalidates the evidence.

Positive evidence ages. A PASSED scan satisfies the gate for 90 days, after which it is stale and denies — the malware signature set that cleared these bytes three months ago may not clear them today. Re-scanning the same immutable bytes produces a fresh attempt. Bumping the scan policy version or the static rule catalog version also invalidates prior PASSED evidence, without deleting any history, which is what "invalidate prior evidence when the policy changes" has to mean for immutable attempts.

Scan findings are persisted redacted and bounded. Detailed scan internals are not exposed to authors: a verdict, a severity, and a rule identity are, and package source never appears in an event, an export, or a log.

Secret hygiene inside a package. A plugin has no legitimate need for a secret in its source. There is no environment, no filesystem, and no way to read platform configuration, so an embedded credential could only ever be a credential for something else — and the archive is stored, scanned, and readable by the platform. Static rules look for embedded credential material, and the runtime's static guard can deny an entrypoint outright with STATIC_SCAN_DENIED.

Do not emit PII. Runtime output and test reports are persisted. Return correlation ids, enumerated dispositions, and coarse buckets. A duration bucket is not re-identifiable; an exact duration paired with a timestamp often is.

Webhook verification. Platform webhooks carry two headers, x-arvexa-signature and x-arvexa-timestamp. The signed payload is ${timestamp}.${rawBody} where rawBody is the exact bytes received. Use verifyPluginWebhookRequest from @arvexa/sdk; it delegates to the platform's own HMAC primitives, so there is exactly one implementation of the scheme and the comparison is timing-safe.

Two rules make verification work in practice:

  • Pass the raw body, never JSON.stringify of a parsed object. Re-serializing changes key order and whitespace, and is the single most common cause of a false signature mismatch.
  • Fail closed on every ambiguity, including a missing secret. A receiver that has not been configured with a secret must not accept unsigned traffic.

Verification failures are distinguished for a reason: TIMESTAMP_SKEW is usually a clock problem on the receiver, while SIGNATURE_MISMATCH is a wrong secret or a modified body — and only the latter should page anyone. The other reasons are SECRET_MISSING, SIGNATURE_HEADER_MISSING, TIMESTAMP_HEADER_MISSING, and TIMESTAMP_MALFORMED.

Tenant isolation is host-side, from the credential. There is no --organization-id flag and no tenant field in any CLI request, because a client-supplied tenant is exactly the parameter a cross-tenant read would need. Scope is derived from the API key.