Security
The trust boundary
Glyx 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}%'`)There's no linter enforcing this today — parameterization is a discipline the db API's parameter-binding makes easy to follow, not something Glyx checks for you at build time.
Capabilities: the real enforcement mechanism
Every native operation — filesystem, network, db, clipboard, and so on — is
gated by a capability declared in glyx.config.json:
{ "capabilities": { "fs": { "read": ["assets/**"] }, "clipboard": true } }Capabilities are fail-closed: an uninitialized or unconfigured app denies everything by default rather than allowing it. A capability not listed in config is unreachable from JS, full stop — calling it rejects the Promise with a capability error rather than silently no-op'ing.
This is a single process-wide flag set once at startup, not a
per-plugin sandbox. A JS plugin's declared
capabilities array is validated against the app's own capabilities at
load time (an audit check — an undeclared capability gets the plugin
silently excluded from registration, logged but not a startup failure) —
but at runtime, any JS in the process, plugin or main app bundle, can
call any capability the app has enabled. There's no isolation between a
plugin and the rest of your JS.
See the Capabilities reference for the full list and JSON shape of every capability.
Storing secrets
Use the OS keychain via the credentials capability — never store secrets in plain files or db:
import { credentials } from '@glyx-dev/react'
// Store — service defaults to 'glyx' if omitted
await credentials.set('api-key', secretValue, { service: 'my-app' })
// Retrieve — returns null if no entry exists
const key = await credentials.get('api-key', { service: 'my-app' })
// Delete
await credentials.delete('api-key', { service: 'my-app' })This uses Keychain on macOS, Credential Manager on Windows, and Secret Service on Linux.
Network security
network.fetch allows both http:// and https:// unconditionally —
there's no TLS-only enforcement and no config flag to restrict it.
Private/loopback hosts (SSRF) are blocked by default regardless of scheme.
If your app must guarantee TLS, validate the URL scheme yourself before
calling fetch.
Never store API keys, passwords, or tokens in glyx.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
dangerouslySetInnerHTMLor equivalent - Sanitize before rendering with a library like
dompurify(adapted for Glyx's renderer) - Glyx's
Textcomponent does not interpret HTML — it is safe to pass raw strings
Code signing and notarization
There is currently no CLI-driven signing or notarization — glyx package
only takes a target and --installer flag. Code-signing an app for
distribution today means signing the packaged artifact yourself (e.g.
codesign/xcrun notarytool on macOS, signtool on Windows) as a
separate step after glyx package, typically wired into your own CI
pipeline. There's no built-in glyx sign/glyx notarize command yet.
See Packaging for build output details.