Skip to content
This repository was archived by the owner on Jul 7, 2026. It is now read-only.

go4cas/cogworks

 
 

Repository files navigation

Cogworks

Self-hosted backend in a single binary — collections, REST API, auth, realtime, file uploads, server-side hooks. TypeScript on Bun.

This is a fork of vaultbase-sh/vaultbase, originally created by Khalid M. Sheet (@khalidsheet) — all credit for the original project goes to him. This fork's main divergence is a migration of the server framework from Elysia to Hono.

Quick start

# Build
bun install
bun run build           # compiles admin + binary → ./cogworks[.exe]

# Run
./cogworks             # serves on :8091
# Visit http://localhost:8091/_/  → setup wizard

Features

  • Collections with typed fields: text, number, bool, email, url, date, file (multi), relation, select (multi), json, autodate, password (bcrypt), editor (rich text), geoPoint
  • Per-collection real SQL tables (not JSON blobs) — fast queries, native indexes
  • REST API — list/get/create/update/delete with filter, sort, expand (nested — forward and reverse relations via expand=comments_via_post), field projection, skipTotal, batch. Offset pagination by default; opt-in keyset/cursor pagination (?cursor=) for O(log n) deep-page seeks with no COUNT
  • Full-text search — opt-in searchable flag on text/email/url/editor fields builds a SQLite FTS5 index (kept in sync by triggers); query via ?search=terms on the list endpoint
  • Auth — email + password, JWT (admin + user), token refresh, multi-admin
  • Passkeys / WebAuthn — passwordless registration + login for auth-collection users (FIDO2), beside TOTP/MFA; per-user credential management
  • Realtime — WebSocket subscribe per collection, broadcast on CRUD; SSE fallback with resume (Last-Event-ID replays missed events within the realtime.retention_sec window)
  • Files — local FS, size + MIME validation, multi-file fields
  • Hooks page — record event hooks (before/after × Create/Update/Delete), custom HTTP routes (/api/custom/*), cron jobs (UTC, with cronstrue + crontab.guru link). Monaco editor with ctx IntelliSense
  • Logs — JSONL files per UTC day, never deleted. JSONPath search
  • Tracing — opt-in OTLP/HTTP trace export (set otel.endpoint); per-request phase spans to any OpenTelemetry collector (Jaeger, Tempo, …). No SDK, no native deps
  • API docs — auto-generated OpenAPI 3.0 spec at /api/v1/openapi.json (derived from your collections) + a rendered reference at /api/v1/docs. Toggle with the docs.enabled setting
  • Rate limiting — per-IP token bucket, per-rule (path + action + audience). Editable from Settings
  • SMTP — full config + test button. helpers.email() available in hooks/routes/jobs
  • Encrypted fields — AES-GCM via COGWORKS_ENCRYPTION_KEY
  • Backup / restore — SQLite snapshot download/upload
  • Single binary — no native deps. Embedded admin UI (gzip+base64 via Bun macro)

Stack

  • Runtime: Bun
  • Framework: Hono
  • DB: SQLite (bun:sqlite)
  • ORM: Drizzle
  • Admin UI: React 19 + Vite + React Router v7 + Zustand + PrimeReact + Monaco + Quill

Configuration (env vars)

Var Default Notes
COGWORKS_PORT 8091 Listen port
COGWORKS_DATA_DIR ./cogworks_data DB, uploads, logs, secrets
COGWORKS_JWT_SECRET auto-generated Persisted in <dataDir>/.secret
COGWORKS_ENCRYPTION_KEY none Required for encrypted fields. Base64 / hex / 32-char string (32 bytes)
COGWORKS_ENCRYPTION_KEY_OLD none Previous key(s), comma-separated, accepted for decryption during a key rotation. See cogworks rotate-key.
COGWORKS_RATE_* rule-based See Settings → Rate limiting

Production deployment

Cogworks is single-process, single-threaded, and does not terminate TLS or compress responses in-process. Both responsibilities belong to a reverse proxy in front of the binary — nginx, Caddy, or Cloudflare. In-process compression was tried and removed: it blocked the event loop on Bun.gzipSync, regressed RPS by ~14%, and doubled p99.9.

Recommended topology

Client ── HTTPS ──> nginx / Caddy / CF ── HTTP ──> Cogworks :8091
                       │                              │
                       │ TLS termination              │ Bun + SQLite (WAL)
                       │ gzip / brotli                │ Single binary
                       │ HTTP/2                       │
                       │ Rate limit (defense)         │
                       └──────────────────────────────┘

Sample nginx config

upstream cogworks {
    server 127.0.0.1:8091 keepalive 64;
    keepalive_timeout 60s;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    # Compression — handled here, not in Cogworks.
    gzip on;
    gzip_types application/json text/plain;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_vary on;

    # WebSocket realtime
    location /realtime {
        proxy_pass http://cogworks;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_read_timeout 86400s;
    }

    location / {
        proxy_pass http://cogworks;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Sample Caddy config

api.example.com {
    encode gzip

    @ws path /realtime
    reverse_proxy @ws localhost:8091

    reverse_proxy localhost:8091 {
        header_up X-Forwarded-For {client_ip}
        header_up X-Forwarded-Proto {scheme}
    }
}

Backups

# Local file
cogworks backup --to /var/backups/cogworks-$(date +%F).db

# Gzip
cogworks backup --to /var/backups/snap.db --gzip

# S3 / R2 / B2 (creds via env)
AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... \
  cogworks backup --to s3://my-bucket/vb/snap.db

AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... R2_ENDPOINT=https://<accountid>.r2.cloudflarestorage.com \
  cogworks backup --to r2://my-bucket/vb/snap.db --gzip

Snapshots are atomic via SQLite's VACUUM INTO — concurrent writers are serialised but not blocked, and the resulting .db file is self-contained (no *-wal / *-shm sidecars to copy alongside). The HTTP GET /api/admin/backup download uses the same VACUUM INTO snapshot (streamed), so it captures un-checkpointed WAL commits and never reads a torn file. Cron-friendly:

30 3 * * * cogworks /usr/local/bin/cogworks backup --to s3://bucket/vb/snap-$(date +\%F).db --gzip --quiet

Restore via POST /api/admin/restore with the snapshot bytes (admin only) or by replacing the running DB file while the service is stopped.

Cluster mode (multi-process)

A single Bun process is single-threaded — caps at one CPU core. For higher throughput on multi-core hosts, use the cluster orchestrator:

# Auto: one worker per available CPU core
bun src/cluster.ts

# Or explicit count
COGWORKS_WORKERS=4 bun src/cluster.ts

# Same via npm script
bun run start:cluster

The parent process spawns N workers, all sharing port COGWORKS_PORT via Bun.serve({ reusePort: true }). The kernel load-balances incoming connections across workers. Workers run identical code; SQLite WAL handles concurrent readers natively.

Health check: GET /_/health (liveness) returns the responding worker's id + pid + uptime — useful to verify load balancing.

Readiness: GET /_/ready returns 200 once the DB is reachable and migrations are applied, 503 otherwise — point a k8s/LB readiness gate here (and liveness at /_/health) so traffic holds off a pod until its schema is migrated during a rolling deploy.

Graceful shutdown: SIGTERM / SIGINT to the parent broadcasts to workers; each drains its log buffer and closes its DB handle. 30s timeout, then SIGKILL.

Crashed worker → automatic restart with 1s backoff.

Platform notes:

  • Linux: full SO_REUSEPORT load balancing. ~Nx throughput on N cores (real ~0.85x — some contention is unavoidable).
  • macOS: SO_REUSEPORT works since macOS 10.10. Same scaling as Linux.
  • Windows: SO_REUSEPORT semantics differ — connections aren't distributed by the kernel; one worker tends to win all accepts. Cluster mode runs but does not multiply throughput on Windows. Use it for fault-tolerance (worker auto-restart) only; deploy on Linux for performance.

SQLite under cluster mode (Phase 6a — current): All workers open the same DB file. WAL allows concurrent readers; writes serialize on the file lock. Read-heavy workloads scale near-linearly with worker count. Write-heavy workloads may see lock contention — measure before adopting the dedicated-writer-process pattern.

Operational notes

  • Run Cogworks under a process supervisor (systemd, runit, pm2). It does not daemonize itself.
  • Back up <dataDir>/ periodically. The single-file SQLite DB is the source of truth — nothing else lives outside.
  • Set COGWORKS_JWT_SECRET explicitly in production. The auto-generated fallback is per-host and will rotate if <dataDir>/.secret is wiped, invalidating every issued token.
  • Set COGWORKS_ENCRYPTION_KEY if you use encrypted fields. Loss of the key permanently corrupts those columns.

Cross-compile

bun run build:linux-x64
bun run build:macos-arm64
bun run build:windows-x64
# or all five
bun run build:all

Output: releases/cogworks-<target>[.exe]

Development

bun run dev         # backend on :8091
bun run dev:admin   # admin on :5173 (proxies /api → :8090)

bun test            # backend tests
bun run typecheck

Note on local bun run build

The compile pipeline patches node_modules/imagescript/codecs/node/index.js in place to stub the native encoder (Bun --compile cannot bundle dlopen-able .node binaries — the patched stub keeps top-level imports working in the single-file binary; JPEG/GIF encode paths fall through to PNG / @jsquash codecs).

Side effect: after running any bun run build:*, the imagescript native encoder is gone from node_modules and 2 GIF/JPEG-encode tests will fail locally. Restore with:

bun install --force

CI is unaffected (every workflow run installs fresh).

License

MIT — see LICENSE. This fork retains the original Vaultbase copyright (Khalid M. Sheet / vaultbase-sh contributors) alongside the fork's own.

About

Self-hosted backend in a single binary.

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages

  • TypeScript 81.7%
  • JavaScript 14.7%
  • CSS 1.3%
  • HTML 1.1%
  • Shell 1.0%
  • MDX 0.1%
  • Astro 0.1%