Security Guidelines

Every plugin you publish runs inside a tenant's VeloCMS instance and has access to real user data. These guidelines are not suggestions — violations get plugins removed from the marketplace without appeal, and repeat offenders lose developer account access permanently.

What plugins cannot do

The following are hard blocks enforced by the automated review pipeline and (in V2) by the isolated-vm runtime. Attempting any of these is an automatic rejection.

  • eval(), new Function(), or any dynamic code execution. Static analysis detects these. There is no exception.
  • child_process.exec(), spawn(), or any process spawning. Plugins cannot execute system commands.
  • Unbounded filesystem access via fs. In V2 (isolated-vm), fs is not available at all. In V1, the scan flags any fs import in plugin code.
  • Network requests to domains not declared in network_access.allowedDomains. The proxy layer enforces this at runtime.
  • Requiring or importing modules outside the plugin's own bundle. No external npm packages at runtime — bundle everything you need.

Secret and credential handling

Tenant API keys and secrets are stored encrypted (AES-256-GCM) in VeloCMS. When your handler runs, the context injects decrypted values via ctx.settings. That decrypted value exists only in memory during handler execution — it is never logged or persisted elsewhere by the platform.

Your responsibility: never log credential values. This includes error paths. If a Stripe API call fails, log the HTTP status and error code — not the key that caused it. The observability pipeline pattern-matches for api_key, secret, and similar substrings in log lines and will flag them in review.

Network domain allowlist

Declare every external hostname you connect to. The allowlist enforces the least-privilege principle for network access. A plugin that calls api.mailchimp.com should declare exactly "api.mailchimp.com" — not "*.mailchimp.com" or "mailchimp.com". Wildcard patterns are rejected in review.

The network proxy logs every outbound request (host, latency, status code) to the tenant's plugin activity log. Anomalous patterns — requests to undeclared hosts, unusually high call volume, requests during unexpected hook invocations — trigger automated alerts and can result in plugin suspension.

User data access scope

Each capability has a defined access scope:

  • members:read — provides the member email, display name, and subscription tier. It does not include payment card data, IP addresses, or internal platform metadata. Use it for legitimate CRM sync or personalization. Do not use it to build user tracking profiles sold to third parties.
  • content:read— provides published and draft post content for the tenant that installed the plugin. You cannot read another tenant's data. Multi-tenant isolation is enforced at the API layer, not just your code.
  • email:send— allows sending transactional emails via Resend using the tenant's sender domain. Mass unsolicited email is a policy violation that results in immediate suspension.

GDPR and data portability obligations

If your plugin stores data about members — opens, clicks, behavior — that data falls under GDPR when the blog's readers are in the EU. You are the data processor; the tenant is the controller. Your privacy policy must describe what data you collect, how long you retain it, and how to delete it on request. Plugins without a privacy policy URL in their manifest are rejected from the marketplace for EU-facing tenants.

Responsible disclosure

Found a security issue in the VeloCMS platform itself? Report it to [email protected]. We aim to acknowledge within 24 hours and fix critical issues within 72 hours. Do not post publicly before coordinating with us — responsible disclosure gives us time to protect users before details become public.