Security

This guide covers the security features built into Svelar and best practices for hardening your application.


Environment & Secrets

APP_KEY

The APP_KEY is used to encrypt sessions and sign tokens. It must be a strong, random string.

npx svelar key:generate

This generates a 64-character hex key and writes it to .env. Never commit your .env file to version control.

Accessing Secrets in SvelteKit

SvelteKit uses Vite, which does not populate process.env from .env files. Always use SvelteKit's built-in env module:

// hooks.server.ts
import { env } from '$env/dynamic/private';

export const { handle } = createSvelarApp({
  auth,
  secret: env.APP_KEY,
});

Never use process.env for secrets in SvelteKit server code. The CLI (npx svelar) loads .env automatically, but the Vite dev server does not.

Secret Rotation

When rotating APP_KEY:

  1. Set the new key in .env
  2. Restart the application
  3. All existing sessions will be invalidated (users must re-login)

Authentication

Session Security

Svelar sessions use DatabaseSessionStore by default (persists across restarts). Session cookies are configured with:

  • httpOnly: true — not accessible via JavaScript
  • secure: true in production — only sent over HTTPS
  • sameSite: 'lax' — CSRF protection for cross-origin requests
  • Configurable lifetime (default: 2 hours)

Password Hashing

Svelar supports three hashing drivers:

Driver When to use
scrypt Default, built into Node.js, no native deps
bcrypt Industry standard, requires bcrypt package
argon2 Strongest, requires argon2 package
Hash.configure({ driver: 'scrypt' });

All drivers use safe defaults (salt generation, appropriate work factors). Never store passwords in plain text.

JWT & Refresh Tokens

  • Access tokens are short-lived (default: 15 minutes)
  • Refresh tokens are long-lived and stored in the database
  • Refresh tokens are single-use (rotated on each refresh)
  • Revoked tokens are rejected immediately

API Token Security

  • API keys are hashed before storage (only the prefix is stored in plain text for lookup)
  • Tokens can be scoped to specific permissions
  • Tokens can have expiration dates
  • Use Authorization: Bearer <token> header

Middleware Pipeline

The default middleware stack provides layered security:

1. Origin Validation (OriginMiddleware)

Rejects requests from unauthorized origins. Prevents DNS rebinding attacks.

2. Rate Limiting (RateLimitMiddleware)

Prevents brute-force and abuse:

new RateLimitMiddleware({
  maxRequests: 100,
  windowMs: 60_000, // 100 requests per minute
})

3. CSRF Protection (CsrfMiddleware)

Validates CSRF tokens on state-changing requests (POST, PUT, DELETE). Automatically excluded for:

  • API routes using Bearer token auth
  • Internal API paths (/api/internal/)

4. Request Signatures (SignatureMiddleware)

For API-to-API communication, verify request integrity with HMAC signatures:

new SignatureMiddleware({
  secret: env.INTERNAL_SECRET,
  paths: ['/api/internal/'],
})

CORS

Configure CORS headers in your middleware:

new CorsMiddleware({
  origins: ['https://yourdomain.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  credentials: true,
})

Never use origins: ['*'] in production with credentials: true.


Permissions & Authorization

Svelar includes a Spatie-inspired permission system:

  • Roles group permissions (e.g., admin, editor)
  • Permissions define granular access (e.g., manage-users, edit-posts)
  • Roles can be assigned to users via pivot tables
  • Direct permissions can be granted to individual users
  • Gate checks enforce authorization at the controller level
// Check in controllers
if (!await user.hasPermission('manage-users')) {
  return json({ error: 'Forbidden' }, { status: 403 });
}

Docker & Infrastructure Security

Port Exposure

Only the application port should be exposed to the host. All backing services (database, Redis, Soketi, Gotenberg) communicate over the internal Docker network.

Service Exposed to host? Notes
app Yes (port 3000) The only public-facing service
postgres/mysql No Internal only
redis No Internal only, password required
soketi No Uncomment port if browser clients connect directly
gotenberg No Internal only
rustfs S3 API No Internal only (app connects via Docker network)
rustfs console Yes (port 9001) Admin dashboard — protect with firewall
meilisearch No Internal only (opt-in with --meilisearch)

Redis Authentication

Redis is configured with password authentication by default:

redis:
  command: redis-server --requirepass ${REDIS_PASSWORD:-svelarsecret}

Change REDIS_PASSWORD in .env before deploying to production.

Database Credentials

Default credentials in docker-compose.yml are for development only. Before deploying:

  1. Set strong passwords in .env for DB_PASSWORD, REDIS_PASSWORD, RUSTFS_ROOT_PASSWORD
  2. Never use default passwords in production
  3. Consider using Docker secrets or a vault for sensitive values

RustFS Console

The RustFS admin console (port 9001) provides full access to object storage. In production:

  • Restrict access via firewall rules (allow only admin IPs)
  • Or remove the port mapping entirely and use the CLI for administration
  • Change RUSTFS_ROOT_USER and RUSTFS_ROOT_PASSWORD from defaults

Input Validation

Always validate input at the boundary using Zod schemas:

import { z } from 'zod';

export const CreatePostSchema = z.object({
  title: z.string().min(1).max(255),
  content: z.string().min(1),
  published: z.boolean().default(false),
});

Use FormRequest classes for automatic validation in controllers. Never trust client-side validation alone.


SQL Injection Prevention

The Svelar ORM uses parameterized queries by default. All query builder methods automatically escape values:

// Safe — values are parameterized
const user = await User.query().where('email', email).first();

// If using raw queries, always use parameters
const results = await Connection.raw('SELECT * FROM users WHERE email = ?', [email]);

Never interpolate user input into raw SQL strings.


Production Checklist

Before deploying to production:

  • Generate a strong APP_KEY with npx svelar key:generate
  • Set unique JWT_SECRET and INTERNAL_SECRET
  • Change all default passwords (DB_PASSWORD, REDIS_PASSWORD, RUSTFS_ROOT_PASSWORD, MEILI_MASTER_KEY)
  • Set NODE_ENV=production
  • Enable HTTPS (use a reverse proxy like Traefik or Nginx)
  • Restrict CORS origins to your domain
  • Configure rate limiting appropriate for your traffic
  • Remove or firewall the RustFS console port (9001)
  • Review exposed Docker ports — only app:3000 should be public
  • Set up log rotation to prevent disk exhaustion
  • Enable database backups
  • Never commit .env files to version control

Reporting Vulnerabilities

If you discover a security vulnerability in Svelar, please report it responsibly by opening a private issue on GitHub. Do not disclose security issues publicly until a fix is available.

Svelar © 2026 · MIT License