🚧 VeloxKit is pre-release software. APIs may change before v1.0. Get started β†’
Documentation
Guides
Security

Security

The trust boundary

VeloxKit has a clear trust boundary between JavaScript and Rust:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  JavaScript (your app code) β”‚  ← untrusted user input lives here
β”‚  React, business logic      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  ← capability-gated bridge
β”‚  Rust (native runtime)      β”‚  ← trusted, validated
β”‚  File system, SQLite, AI    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The Rust layer validates all inputs from JavaScript before executing native operations. SQL parameters are always parameterized β€” the db API does not support raw SQL interpolation from user input.

JS vs Rust: decision tree

Keep in JavaScript:

  • Business logic, UI state, data transformation
  • Anything that benefits from React reactivity
  • Non-sensitive computation

Move to Rust (native extensions) if:

  • You need to process large files (>10MB) without blocking the UI thread
  • You need cryptographic operations beyond what JS provides
  • You need to interface with C libraries directly
  • Performance-critical loops that run thousands of times per second

See Native Extensions for how to write Rust extensions.

SQL injection prevention

Always use parameterized queries. Never interpolate user input into SQL:

// βœ“ Safe β€” parameterized
const results = db.query('SELECT * FROM notes WHERE title LIKE ?', [`%${userInput}%`])
 
// βœ— Unsafe β€” never do this
const results = db.query(`SELECT * FROM notes WHERE title LIKE '%${userInput}%'`)

The second form will throw a lint error in VeloxKit's ESLint plugin (eslint-plugin-veloxkit).

Storing secrets

Use the OS keychain via the credentials capability β€” never store secrets in plain files or db:

import { credentials } from 'veloxkit'
 
// Store
await credentials.set('my-app', 'api-key', secretValue)
 
// Retrieve
const key = await credentials.get('my-app', 'api-key')
 
// Delete
await credentials.delete('my-app', 'api-key')

This uses Keychain on macOS, Credential Manager on Windows, and libsecret on Linux.

Network security

All network.fetch calls use TLS by default. To allow plain HTTP in development:

// veloxkit.config.ts
dev: {
  allowInsecureHttp: true,  // development only, ignored in production builds
}
🚫

Never store API keys, passwords, or tokens in veloxkit.config.ts, source code, or the SQLite database. Use credentials (OS keychain) for all secrets.

Content from untrusted sources

If your app renders content fetched from remote sources (user-generated content, markdown, etc.):

  • Do not use dangerouslySetInnerHTML or equivalent
  • Sanitize before rendering with a library like dompurify (adapted for VeloxKit's renderer)
  • VeloxKit's Text component does not interpret HTML β€” it is safe to pass raw strings

Code signing and notarization

For public distribution:

  • macOS: notarize with veloxkit notarize. Requires an Apple Developer account and the identifier field in config.
  • Windows: sign with veloxkit sign --cert path/to/cert.pfx. Unsigned apps show SmartScreen warnings.
  • Linux: not required but AppImage signing is supported.

See Packaging for complete instructions.