Multi-tenant field-service SaaS for dispatching crews, scheduling jobs, tracking time, and managing parts inventory. Built as a pnpm monorepo with a NestJS API, Next.js web app, BullMQ worker, and a Capacitor shell that wraps the web build for iOS and Android. Each tenant gets its own Postgres schema reached at a wildcard subdomain, with per-tenant data encrypted via Vault transit keys.
// features
- Per-tenant subdomain routing with schema-per-tenant isolation
- Quotes-to-jobs flow with scheduled start/end and team assignment
- Time entries with clock-in source, daily rollups, and audit trail
- Parts, suppliers, and multi-location inventory with reserved quantities
- Background worker for email, SMS, PDF, and webhooks
- Superuser console to provision tenants and edit any tenant's users
- Single-command idempotent Linode deploy via shell + Ansible, no containers
// interesting details
- Field-level encryption: PII columns are stored as bytea blobs sealed with AES-256-GCM, where each write fetches a fresh data-encryption key from Vault transit and the wrapped DEK is prepended to the ciphertext. Decrypts cache the unwrapped DEK for five minutes, keyed by SHA-256 of the wrapped blob.
- Containerless deploy: ops/deploy.sh + systemd unit files + an nginx wildcard SSL config bring up the whole stack on a single Linode box, with an optional --harden-ssh flag that disables root login and password auth.
- Realtime layer uses Socket.IO with the Redis adapter so the API can run multi-process behind nginx.
- Tenancy lives in the database, not in code: a public schema holds tenants, global_users, and memberships; each tenant gets its own schema (tenant_<tid>) that migrations apply by switching search_path.
// tech stack
TypeScript
Node 22
NestJS 10
Next.js
Capacitor (iOS + Android)
Drizzle ORM
Postgres 16
Redis 7 + BullMQ
Socket.IO
HashiCorp Vault
Tailwind
Ansible / nginx / systemd
Want to collaborate on sendafield?
The repo is private — drop a note and tell us what brought you here.