Skip to content

Quickstart

  • Bun ≥ 1.4 — runtime, package manager and test runner for the whole repository.
  • Docker — for the local Postgres.
  • Python 3.13 + uv — only for services/ai.
  • Rust toolchain — only for apps/native (the Tauri shells).

Every command below is the same in bash and in PowerShell.

Terminal window
bun install
cp .env.example .env # API, db tooling, compose
cp apps/web/.env.example apps/web/.env

BETTER_AUTH_SECRET ships empty — a placeholder secret is the one thing that must not have a default, and the API refuses to boot without it. Generate one and put it in .env:

Terminal window
bun -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Bun is used rather than openssl because it is the one generator every machine running this repo already has; PowerShell has no openssl. Then:

Terminal window
bun run db:up # Postgres 17 + pgvector in Docker (127.0.0.1:5433 by default)
bun run db:migrate # applies packages/db/drizzle/*.sql (exits 1 if any stay pending)
bun run dev:app # api on :3000 + web on :5173

Then open http://localhost:5173. Apart from that one secret, the defaults in .env.example are enough for the local loop: emails are printed to the API terminal, billing and social sign-in stay off, and nothing is capped. See Configuration for what each variable switches on.

Command What
bun run dev every app with a dev script (api, web, site :4321, docs :4322)
bun run dev:desktop Tauri desktop shell around the dev server (needs Rust)
bun run verify lint, typecheck and tests, in CI’s order — before every push
bun run check · bun run test · bun run lint · bun run build the same steps one at a time, plus the build. test runs Svelte components in a real browser, so it installs Playwright’s Chromium on first run
bun run test:e2e Playwright against a production preview of apps/web
bun run admin:create -- --email … [--name …] create the first platform administrator; the password comes from ADMIN_PASSWORD or a prompt
bun run seed demo organization, workspaces, documents and jobs, so a fresh install does not open on six empty states
cd services/ai && uv sync && uv run uvicorn ai.main:app --reload the Python service on :8000

The full list is in Commands.

  1. Sign up at /signup with email and password. In development the verification email is printed to the API terminal; REQUIRE_EMAIL_VERIFICATION decides whether a session is issued before the address is verified (default: only in production).
  2. Create an organization. It is the tenant and billing entity that owns members, invitations and teams. New sessions start in your first organization; the sidebar switcher changes it.
  3. Invite people from /app/team — invitations are emailed as links that expire after 7 days — and give them the owner, admin or member role.
  4. Create a workspace from /app. Workspaces are the product container, scoped to the organization and optionally to a team.

Platform administrators are users whose role is admin — independent of organization roles. Create the first one from the command line, then promote others from /admin/users:

Terminal window
ADMIN_PASSWORD='' bun run admin:create -- --email you@example.com --name 'You'
# add --yes to promote an account that already exists

With ADMIN_PASSWORD unset the script prompts for one. The password is never read from the command line, where it would stay in shell history and be visible in ps.

A fresh database opens on empty states, which makes a broken feature hard to tell from an unpopulated one. bun run seed creates a demo organization with a team, two workspaces, three documents, six finished jobs with their credit-ledger rows and an audit trail, owned by demo@example.com / demo-password-1234 (SEED_PASSWORD overrides). It is idempotent, it refuses to run with NODE_ENV=production, and it writes no storage objects — so a seeded document can be read and searched but not downloaded.

apps/
web/ SvelteKit application — auth, orgs/teams, workspaces, billing, admin (thin client)
api/ Hono on Bun — Better Auth, oRPC router (RPC + REST/OpenAPI), webhooks
site/ Astro — marketing, pricing, blog, SEO
docs/ Astro Starlight — this documentation
native/ Tauri 2 — desktop + mobile shells around apps/web's static build (no IPC)
packages/
api-contract/ oRPC + Zod contract: the API's single source of truth
api-client/ typed client for the contract (browser, SSR, Tauri, Bun scripts)
auth/ Better Auth server instance (API) and Svelte client factory (frontends)
db/ Drizzle schema (generated auth tables + ours), migrations, Bun.SQL client
billing/ plan catalog + Stripe client
email/ provider abstraction (Resend / console) + templates
env/ validated server environment (t3-env + Zod)
storage/ document bytes: local disk with signed URLs, or S3/R2/MinIO
ui/ shared Svelte 5 components (shadcn-svelte/bits-ui + thin form wrappers) + Tailwind tokens
tsconfig/ shared TypeScript configs
services/
ai/ FastAPI service (uv) — internal, called by the API only
infra/
compose.yml self-hosted stack: postgres · api · web · ai · caddy
compose.dev.yml Postgres for local development
caddy/ subdomains.Caddyfile (public) · single-origin.Caddyfile (tailnet/LAN)
docker/ Dockerfile.static (Astro sites → Caddy)

Internal packages are consumed from source — no build step. Shared dependency versions are pinned once in the root package.json catalog and referenced with catalog:.