Overview

How Voltro handles security — reporting a vulnerability, the supply-chain gates (dependency audit, inbound-license allowlist, SBOM), supported versions, and why self-hosting keeps your data yours.

Security is a first-class concern, not an afterthought. This page is the public summary of how we handle it; the machine-readable policy also ships as a SECURITY.md inside every @voltro/* package.

Reporting a vulnerability

Please report suspected vulnerabilities privately — never in public issues or channels.

  • Email support@voltro.dev with the subject SECURITY.
  • Include the affected package(s) and version(s), the impact, and a minimal reproduction if you have one.

You'll get an acknowledgement within 3 business days, an assessment once we've reproduced the issue, and a coordinated disclosure timeline agreed with you — with credit in the release notes if you'd like it. We don't run a paid bug-bounty program yet, but we genuinely value responsible disclosure.

Outbound HTTP is SSRF-guarded by default

The HttpClient your handlers yield* refuses internal targets:

  • loopback, RFC-1918, CGNAT and link-local addresses — including the 169.254.169.254 cloud-metadata endpoint
  • the hostnames localhost, *.internal, *.local
  • any non-http(s) scheme

Every redirect hop is revalidated, not just the URL you passed. A public URL that 302s to the metadata endpoint is the actual attack; checking only the initial target catches none of it.

This matters because a caller-supplied URL is ordinary product surface — a scraper, a webhook-registration form, an importer, a "test this connection" button. Those reach HttpClient with whatever the user typed.

Allowing a target on purpose

// app.config.ts
export default defineApiConfig({
  http: { allowHosts: ['*.svc.cluster.local', 'billing.internal'] },
})

An entry may be an exact host, a *.suffix wildcard (which does not match the apex — one that did would silently widen your exception), or host:port when only one port should be reachable.

There is no boolean off-switch, deliberately: "we call one internal service" and "we do not check URLs" are different postures, and a boolean cannot tell them apart six months later.

In tests, allow the stub — don't mock the guard

Use the same allowHosts to reach a local stub server (['127.0.0.1:8787']). That keeps the test on the real guarded code path with a narrow exception. Mocking the guard away instead means the production path is never exercised — which is exactly how this framework's own webhook delivery once ended up behind a NODE_ENV check.

What this does NOT cover

DNS is not resolved. A public hostname that resolves to a private address (DNS rebinding) still passes. That vector needs network-layer egress control; it is stated here rather than silently implied.

Supply-chain assurance

Every release passes automated supply-chain gates in CI before a single package is published:

  • Dependency audit. The production dependency graph is audited on every build (pnpm audit --prod); a HIGH or CRITICAL advisory fails the release. Known-but-unfixed-upstream transitives are pinned deliberately, not waved through.
  • Inbound-license allowlist. Every third-party runtime dependency's license is checked against an allowlist — a disallowed license fails the release. The aggregated attributions ship as THIRD-PARTY-NOTICES.md in each package.
  • SBOM. A CycloneDX Software Bill of Materials of the third-party runtime graph is generated for, and attached to, each release. If your procurement or security team needs it, request it at support@voltro.dev.
  • Release integrity. Publishing is gated behind organization 2FA and a narrowly-scoped, short-lived automation token, and is all-or-nothing (two-phase) so consumers never see a half-published release.

Supported versions

Voltro is pre-1.0 and ships in lockstep — all @voltro/* packages share one version. Only the latest published release receives security fixes. Pin exact versions in production and upgrade promptly.

Your data stays yours

Voltro is self-hosted: it runs next to your database, with your connection string, your backups, and your access controls. When you self-host, the framework never proxies your data through Voltro infrastructure — there is no phone-home and no hosted control plane in the request path; the runtime is a library that lives in your own deployment.

Managed Voltro Cloud is coming as a separate, opt-in deploy target. Choosing it means running your workload on Voltro-operated infrastructure under its own data-processing terms — a deliberate choice, not the default. Self-hosting remains the default and keeps the guarantees above.