- TypeScript - For type safety and improved developer experience
- TanStack Start - SSR framework with TanStack Router
- Mantine - React component library (
@mantine/core,@mantine/form, charts, dates, schedule, notifications, modals) - Shared UI package - Mantine
MantineProvider, theme, and color-scheme helpers live inpackages/ui - Drizzle - TypeScript-first ORM
- PostgreSQL - Database engine
- Authentication - Better-Auth
- Vite+ - Unified runtime, package, build, lint, format, and test tooling
First, install the dependencies:
vp installThis project uses PostgreSQL with Drizzle ORM.
-
Make sure you have a PostgreSQL database set up.
-
Update your
apps/web/.envfile with your PostgreSQL connection details. -
Apply the schema to your database:
vp run db:pushThen, run the development server:
vp run devOpen http://localhost:3001 in your browser to see the fullstack application.
React web apps in this stack share Mantine setup through packages/ui.
- Tweak the theme in
packages/ui/src/theme.ts - Adjust the provider (color scheme, modals, notifications) in
packages/ui/src/provider.tsx - Edit global styles in
packages/ui/src/styles/globals.css - Mantine PostCSS preset config lives in
packages/ui/postcss.config.cjs
Import shared exports like this:
import { Provider as UIProvider } from "@prive-admin-tanstack/ui/provider"
import { theme } from "@prive-admin-tanstack/ui/theme"
import "@prive-admin-tanstack/ui/globals.css"Mantine components are imported directly from @mantine/* packages in app code:
import { Button } from "@mantine/core"- Format and lint fix:
vp run check
prive-admin-tanstack/
├── apps/
│ └── web/ # Fullstack application (React + TanStack Start)
├── packages/
│ ├── api/ # Shared API layer
│ ├── auth/ # Authentication configuration & logic (Better-Auth)
│ ├── config/ # Shared TS / tooling config
│ ├── db/ # Database schema & queries (Drizzle)
│ ├── env/ # Shared env loading & validation
│ └── ui/ # Mantine provider, theme, and global styles
vp run dev: Start all applications in development modevp run build: Build all applicationsvp run dev:web: Start only the web applicationvp run check-types: Check TypeScript types across all appsvp run db:push: Push schema changes to databasevp run db:generate: Generate database client/typesvp run db:migrate: Run database migrationsvp run db:studio: Open database studio UIvp run compose:up/compose:stop/compose:down/compose:watch: Manage local dev compose stack (Postgres + MinIO)vp run check: Run Oxlint and Oxfmt
The production stack runs on VPS prive via Docker Compose. The
authoritative deploy doc is docs/deploy/vps-setup.md.
-
Pull requests to
mainrun source and Docker build checks (.github/workflows/pr-build.yml). Merges tomaindo not deploy. -
Creating a semver tag such as
v1.4.0from a commit onmainruns the release workflow, publishesghcr.io/<repo>-{web,server,migrate}images tagged with{latest,<commit-sha>,v1.4.0}, and deploys that tag to the VPS (.github/workflows/release.yml). -
All runtime secrets live in the 1Password vault
prive-admin, spread across three items:prive-admin-prod(app, postgres, infra),Cloudflare R2(R2 keys + bucket), andtailscale-oauth(TS OAuth). The workflow pulls them at deploy time using a service-account token (OP_SERVICE_ACCOUNT_TOKEN— the only remaining GitHub Actions secret). -
Rollback to a previous image:
ssh root@prive 'cd ~/prive-admin && IMAGE_TAG=<old-tag-or-sha> docker compose up -d web'