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),fsis not available at all. In V1, the scan flags anyfsimport 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.