Skip to content

Database Schema

SkySend uses SQLite with Drizzle ORM. The database file is located at data/skysend.db.

SQLite Configuration

sql
PRAGMA journal_mode = WAL;        -- Concurrent reads + serialized writes
PRAGMA busy_timeout = 5000;       -- Wait up to 5s on lock contention
PRAGMA synchronous = NORMAL;      -- Safe with WAL, better write performance
PRAGMA foreign_keys = ON;         -- Enforce referential integrity

WAL (Write-Ahead Logging) mode allows concurrent reads while writes are serialized. This is more than sufficient for a single-instance self-hosted service.

Tables

uploads

ColumnTypeDefaultDescription
idTEXT (PK)-UUID v4
ownerTokenTEXT NOT NULL-Base64url-encoded owner token
authTokenTEXT NOT NULL-Base64url-encoded auth token
saltBLOB NOT NULL-HKDF salt (16 bytes)
encryptedMetaBLOBNULLAES-256-GCM encrypted metadata
nonceBLOBNULLMetadata IV (12 bytes)
sizeINTEGER NOT NULL-Total payload size in bytes
fileCountINTEGER1Number of files (1 = single, >1 = archive)
hasPasswordINTEGER0Whether password protection is active
passwordSaltBLOBNULLPassword KDF salt (16 bytes)
passwordAlgoTEXTNULL"argon2id" or "pbkdf2"
maxDownloadsINTEGER NOT NULL-Maximum allowed downloads
downloadCountINTEGER0Current download count
expiresAtTIMESTAMP NOT NULL-Expiry timestamp (Unix epoch)
createdAtTIMESTAMPcurrent_unix_timeCreation timestamp
storagePathTEXT NOT NULL-Filename on disk (UUID.bin)

Indexes

IndexColumnPurpose
idx_uploads_expires_atexpiresAtEfficient cleanup queries

Concurrency

Download count updates (downloadCount = downloadCount + 1) are atomic SQL operations that hold the write lock for microseconds. WAL mode allows thousands of such writes per second while reads are never blocked.

The download route uses an atomic SQL update with a WHERE condition to prevent race conditions:

sql
UPDATE uploads
SET downloadCount = downloadCount + 1
WHERE id = ? AND downloadCount < maxDownloads

If maxDownloads is already reached, the update affects zero rows and the request is rejected.

Migrations

Database migrations are managed by Drizzle ORM and stored in apps/server/src/db/migrations/. Migrations run automatically on server startup.