# Tessera Open-source ticketing system — Request Tracker's paradigm rebuilt in modern TypeScript. ## Architecture ``` tessera/ ├── src/ # Backend: Bun + Hono + Drizzle ORM │ ├── index.ts # Hono server entry (port 9876) │ ├── config.ts # Zod-validated env config │ ├── db/ # Drizzle ORM schema + migrations │ ├── routes/ # REST API endpoints │ ├── models/ # TypeScript types + Zod schemas │ ├── scrip/ # Scrip engine (prepare/commit two-phase) │ └── lifecycle/ # State machine validator ├── web/ # Frontend: Next.js 16 + shadcn/ui │ ├── src/app/ # App Router pages │ ├── src/components/ # Reusable components + widgets │ └── src/lib/ # API client + types + utils ├── drizzle/ # SQL migration files └── docs/ # Architecture + design specs ``` ## Stack **Backend:** Bun runtime, Hono web framework, Drizzle ORM, PostgreSQL 17, Zod validation, Handlebars templates, nodemailer **Frontend:** Next.js 16 App Router (Turbopack), shadcn/ui (Tailwind CSS), next-themes, date-fns, lucide-react icons **Fonts:** Inter (variable), JetBrains Mono ## Running Locally ### Prerequisites - Bun (`nix-shell -p bun` or install globally) - Node.js 22+ (`nix-shell -p nodejs_22`) - Docker (for PostgreSQL) - PostgreSQL container: `docker run -d --name tessera-db -e POSTGRES_USER=tessera -e POSTGRES_PASSWORD=*** -e POSTGRES_DB=tessera -p 127.0.0.1:5433:5432 postgres:17-alpine` ### Start backend ```bash cd ~/projects/tessera cp .env.example .env npm run dev:backend # Starts API on port 9876 ``` ### Run migrations ```bash npm run db:migrate npm run db:seed # Demo data npm run db:seed:reset # Reset + re-seed ``` ### Start frontend ```bash cd web npm install # Use npm, NOT bun bun run dev # Dev server on 127.0.0.1:3100 (HMR) ``` ## API Endpoints All endpoints on port 9876. Frontend proxies `/api/*` via `next.config.ts`. | Method | Path | Description | |--------|------|-------------| | GET | /health | Health check | | GET | /tickets | List tickets (?queue_id=&status=&owner_id=&team_id=&q=&limit=&cf.*=) | | POST | /tickets | Create ticket | | GET | /tickets/:id | Get ticket with custom fields | | PATCH | /tickets/:id | Update ticket (validates lifecycle, runs scrips, returns scrip_results) | | POST | /tickets/:id/preview | Dry-run scrips for status change | | POST | /tickets/:id/comment | Add comment to ticket | | GET | /tickets/:id/transactions | List ticket transactions | | GET/POST/PATCH | /queues | CRUD queues | | GET/POST/PATCH/DELETE | /scrips | CRUD scrips | | GET/POST/PATCH | /custom-fields | CRUD custom fields | | GET/POST/PATCH | /lifecycles | CRUD lifecycles | | GET/POST/PATCH/DELETE | /users | CRUD users | | GET/POST/PATCH/DELETE | /templates | CRUD templates + POST /preview | | GET/POST/PATCH/DELETE | /views | CRUD saved views | | GET/POST/PATCH/DELETE | /teams | CRUD teams + POST/DELETE members | | GET/POST/PATCH/DELETE | /dashboards | CRUD dashboards + widgets + widget data | ## Key Design Decisions - **Ticket IDs are sequential integers** (1, 2, 3...), formatted as `TKT-0001` for display. No UUIDs. - **Transaction-centric:** Every state change creates a transaction record. The scrip engine runs on transactions. - **Two-phase scrip engine:** Prepare (no side effects) then Commit (execute actions). Supports dry-run mode. - **Lifecycle state machines:** Per-queue configurable status transitions with wildcard support. - **SQL-level filtering:** Ticket filters (status, queue, owner, team, custom fields) pushed to PostgreSQL via Drizzle WHERE clauses. - **No ORM for frontend:** Drizzle is only on the backend. Frontend uses a typed fetch wrapper (`web/src/lib/api.ts`). - **Dev server over production:** Use `bun run dev` (port 3100) with HMR. Build+restart only when dev server has issues. - **Design consistency:** See `docs/design-system.md` for the design rules applied across the app. ## Git Workflow Repo: `https://git.gjermund.xyz/gjermund/tessera` ```bash git remote set-url origin https://gjermund:TOKEN@git.gjermund.xyz/gjermund/tessera.git ``` ## Common Issues - **Frontend shows skeleton/blank page:** Dev server may have HMR issues. Kill port 3100, rebuild with `npm run build`, restart with `npm run start`. - **Backend not running on 9876:** Restart with `bun run src/index.ts`. Check port with `ss -tlnp | grep 9876`. - **Database connection refused:** Docker container may be stopped. `docker start tessera-db`. - **Build errors after migration:** Run `bun run src/db/migrate.ts` to apply new migrations.