# Draft: Multi-Tenant Club Work Manager ## Requirements (confirmed) - **Multi-tenant**: Multiple clubs (tennis, cycling, etc.) share one application - **Tenant identification**: Credential-based — user logs in, system resolves which club(s) they belong to. No subdomains or URL paths for tenant routing. - **Data isolation**: PostgreSQL Row-Level Security (RLS) — shared database, tenant isolation via RLS policies - **Work items**: Hybrid — both task-based (title, desc, assignee, status) AND schedule/shift-based (time slots, sign-ups, assignments) - **Authentication**: External provider (Auth0, Clerk, or Keycloak) - **Target scale**: MVP / Proof of concept (1-5 clubs, <100 users) - **Backend**: .NET (ASP.NET Core) with PostgreSQL - **Frontend**: Next.js with Bun - **Deployment (prod)**: Kubernetes cluster - **Deployment (local)**: Docker Compose - **New request**: Append CI/CD pipeline planning for the Gitea-hosted repository (`https://code.hal9000.damnserver.com/MasterMito/work-club-manager`) ## Technical Decisions - **Multi-tenancy strategy**: RLS + EF Core global query filters (defense-in-depth) - **Tenant resolution**: NOT subdomain-based. Tenant derived from authenticated user's JWT claims (club membership) - **Bun runtime**: Research shows Bun has P99 latency issues with Next.js SSR in production (March 2026). Use Bun for local dev / package management, Node.js for production runtime. - **Infrastructure**: Kustomize (simpler for MVP, no Helm complexity needed yet) ## Research Findings - **Finbuckle.MultiTenant**: Industry-standard .NET multi-tenancy library. Supports header strategy — good fit for credential-based tenant resolution. - **EF Core Global Query Filters**: Auto-filter all queries by tenant_id. Combined with RLS for defense-in-depth. - **PostgreSQL RLS**: SET app.current_tenant per connection. Must reset when returning connections to pool. - **Next.js + Bun**: Bun has 340ms P99 latency vs 120ms for Node.js (Platformatic benchmarks Jan 2026). Recommendation: Bun for dev, Node.js for prod Docker image. - **K8s deployment**: Kustomize with base + overlays (dev/staging/prod). PostgreSQL as StatefulSet for dev, managed DB for prod. - **Docker Compose**: Hot reload via `dotnet watch` for .NET, standard Next.js dev server. ## Decisions (Round 2) - **Auth provider**: Keycloak — self-hosted in K8s cluster, runs alongside the app - **Multi-club membership**: YES — a user can belong to multiple clubs, needs a club-switcher/selector after login - **Member roles**: 4-tier — Admin, Manager, Member, Viewer (per club, since user can be Admin in one club and Member in another) - **Shift model**: Time-slot with sign-up — date, time range, location, description, required headcount. Members sign up or get assigned. - **Repo structure**: Monorepo — /backend, /frontend, /infra in one repo - **Notifications**: None for MVP — simplest approach, users check the app ## Decisions (Round 3) - **.NET version**: .NET 10 (user explicitly requested) - **Work item statuses**: 5-state — Open → Assigned → In Progress → Review → Done - **Project type**: Greenfield — starting from scratch, empty repo - **UI framework**: Tailwind CSS + shadcn/ui component library ## Test Strategy Decision - **Infrastructure exists**: NO (greenfield) - **Automated tests**: YES (TDD — tests first) - **Backend framework**: xUnit + Testcontainers (PostgreSQL) for .NET 10 - **Frontend framework**: bun test / Vitest + React Testing Library for Next.js - **Agent-Executed QA**: ALWAYS (mandatory — Playwright for UI, curl for API) - **Test infrastructure setup**: Must be part of Wave 1 scaffolding tasks ## Decisions (Round 4) - **Git repository**: Initialize git repo as first step in Task 1 — `git init` + comprehensive `.gitignore` (dotnet + node + IDE) + initial commit ## Decisions (Round 5) - **CI/CD requested**: User wants plan extension for pipeline on Gitea server - **Repository host**: Self-hosted Gitea instance (`code.hal9000.damnserver.com`) - **Pipeline scope**: CI-only (no deployment automation in this extension) - **Release policy input**: User prefers release-tag based trigger if CD is added later - **Registry input**: Gitea Container Registry preferred ## Open Questions - (none blocking — CI scope confirmed; CD trigger/registry captured for future extension) ## Scope Boundaries - INCLUDE: Full backend API, frontend app, Docker Compose, Kubernetes manifests (Kustomize), database schema + EF Core migrations, Keycloak integration, work item CRUD, time-slot shift management with sign-up, club-switcher, role-based access control (4 roles), PostgreSQL RLS, Gitea CI workflow (build/test/lint/manifest validation) - EXCLUDE: Billing/subscriptions, email/push notifications, mobile app, recurring shift patterns (future), custom roles, reporting/analytics dashboard