Full-stack monorepo starter — Hono APIs, SvelteKit frontend, PostgreSQL, deployed to a VPS via Dokku.
apps/
web/ # SvelteKit frontend
server/ # Main Hono/oRPC API
assistant/ # AI assistant service
notifications/ # Queue consumer / email service
packages/
api/ # oRPC contracts + routers + services
api-contract/ # Shared oRPC contract types
assistant/ # Assistant contract, tools, transport
auth/ # Better Auth setup
db/ # Drizzle schema + migrations + seed scripts
env/ # Typed env validation
infra/ # Docker/VPS infrastructure scripts
notifications/ # Queue contracts + processor helpers
proxy/ # Optional local tunnel reverse-proxy
ui/ # Shared Svelte UI components
vitest-config/ # Shared Vitest config
bun install
# Start all services (Hono servers + SvelteKit)
bun run dev
# Frontend only
bun run dev:web
# Backend services only (server + assistant + notifications)
bun run dev:server
Local services use the .env file at the repo root. Copy .env.example (or the bootstrap template) and fill in values.
# Start everything in containers including Postgres, Loki, Grafana, smtp4dev
bun run deploy:docker:local
# Grafana: http://localhost:3110
# smtp4dev (captured emails): http://localhost:5025
bun run lint
bun run check-types
bun run test
bun run build
bun run test:e2e # deployment-gate stories (packages/e2e-web, host-managed services)
bun run test:e2e:docker # deployment-like gate (Docker Compose stack + Playwright)
bun run ci:preflight # one-shot local mirror of CI gates
# Validate workflow schema + expression wiring locally
act -W .github/workflows/ci.yml --validate
act -W .github/workflows/deploy-docker.yml --validate
# Dry-run deploy workflow graph (no real SSH/deploy)
act -W .github/workflows/deploy-docker.yml -n workflow_dispatch --input environment=staging
# Run real deploy job locally through the same workflow definition
# (uses your local secret/var files and real SSH target from those values)
act -W .github/workflows/deploy-docker.yml workflow_dispatch \
-j deploy \
--input environment=staging \
--secret-file .env.staging.secrets \
--var-file .env.staging.vars
packages/e2e-web is the deployment-gate suite used by CI. It runs hardened cross-service user stories against near-production backend startup (start:test, no file watch/HMR).apps/web Playwright is dev-only for local progress checks and fast UI flow validation while building features.bun run test:e2e:docker starts db/server/assistant/notifications/web via Docker Compose (same Dockerfiles as deploy), runs the same packages/e2e-web stories, and tears everything down.# Dev-only checks while iterating on frontend flows
cd apps/web
bun run test:e2e:dev
bun run db:generate # generate Drizzle migrations
bun run db:migrate # run migrations
bun run db:push # push schema without migration files (dev only)
bun run db:seed # seed with test data
bun run db:studio # open Drizzle Studio
VPS bootstrap, Dokku app creation, DNS, and secrets are managed as TypeScript IaC in infra/pulumi/:
bun run infra:preview # dry-run — see what would change
bun run infra:up # apply changes to VPS
Pulumi handles: VPS creation (1gb.ru), Docker + Dokku install, UFW firewall, fail2ban, SSH hardening, sslh (SSH+HTTPS on port 443), Postgres, Let's Encrypt SSL, Cloudflare DNS, and GitHub Actions secrets sync.
Push to main — GitHub Actions builds Docker images, pushes to GHCR, and deploys to Dokku automatically.
Manual deploy from VPS (if needed):
sudo dokku git:from-image <app> ghcr.io/<org>/<repo>/<app>:<tag>
If a release is unhealthy after deploy, rollback immediately:
sudo dokku ps:rollback <app>
Validate rollback:
https://<app-domain>/health returns 200.sudo dokku ps:report <app> --deployed shows the expected prior release.For multi-app incidents, rollback affected apps one-by-one in dependency order (server first, then assistant / notifications / web) and re-check health after each rollback.
This repository is currently in early stage. Keep a simple staging baseline:
Create a backup dump:
sudo dokku postgres:export myapp-db > myapp-db-$(date +%Y%m%d-%H%M%S).sql
Restore from dump:
sudo dokku postgres:import myapp-db < myapp-db-YYYYMMDD-HHMMSS.sql
After restore:
/health) for all services.Note: automation helper scripts for backup/restore are intentionally postponed for now.
Managed automatically by pulumi up (synced via gh secret set).
| Secret | Description |
|---|---|
SSH_HOST |
VPS IP address |
SSH_USER |
SSH user (usually root) |
SSH_PORT |
SSH port (443 via sslh) |
SSH_PRIVATE_KEY |
ED25519 private key for SSH auth |
SSH_PRIVATE_KEY_B64 |
Base64-encoded private key |
SSH_HOST_KEY |
VPS host key for known_hosts |
grafana.${DOMAIN}).ssh -L 3110:localhost:3110 root@vpshttp://localhost:5025. In prod, set SMTP_HOST/SMTP_USER/SMTP_PASS via dokku config:set.