A reusable internal web-app starter built from the extracted platform core of the original product.
Standalone template repo:
https://github.com/TKlerx/webapp-template
Template provenance files:
TEMPLATE_VERSION.md.template-origin.json- refresh helper:
pnpm run template:stamp
These files are intentionally committed into the template so copied repos still retain a visible upstream baseline even if Git history is removed.
- Next.js 16 app router
- Prisma starter data model with SQLite for local development
- PostgreSQL-backed Docker deployment
- Go-based
starterctlCLI with PAT-backed API access and GoReleaser packaging - uv-managed Python worker skeleton for background jobs
- Azure SSO and local login
- mail abstraction layer with Microsoft Graph-backed shared mailbox read/send support
- role-based access control
- user administration
- dashboard home screen
- audit trail and export
- i18n, theme toggle, responsive UI
- Vitest, Playwright, Semgrep, duplication checks
- repo-local pnpm and uv dependency cooldown policy
- continuity workflow with
CONTINUE.mdandCONTINUE_LOG.md
The repo baseline is documented in specs/base/:
specs/base/architecture.mdspecs/base/runtime-and-ops.md
Read those files before creating new product-specific specs.
Treat this repo as the upstream template for downstream apps created from it.
Recommended workflow:
- Fix shared bugs in the downstream app where you discovered them.
- Port the generic part of that fix back into this template repo as a small, focused commit.
- Add or update tests in this template for that shared behavior.
- Re-apply the same fix to other downstream apps by cherry-picking the focused commit or porting the same small diff.
Guidelines:
- Keep shared subsystems in similar paths across apps when possible.
- Separate template-worthy fixes from app-specific feature work.
- Prefer small commits such as
fix(auth): ...over large mixed changes. - If a subsystem like auth needs repeated cross-app fixes, consider extracting it into a shared package later.
- Downstream apps should keep
TEMPLATE_VERSION.mdand.template-origin.jsonso they know which upstream template commit they are based on. - After creating a downstream app or after pulling upstream template fixes, run
pnpm run template:stampand commit the updated provenance files.
.\validate.ps1 all
.\validate.ps1 e2e
.\validate.ps1 fullPlaywright E2E defaults to a local PostgreSQL container named
webapp-template-e2e-postgres on host port 55432. The E2E setup script
creates or starts that container, ensures the business_app_starter_e2e_test
database exists, resets that database, seeds the initial admin, and runs the app
with the Postgres Prisma schema. Use a separate database such as
business_app_starter_manual in the same container for manual exploratory
testing. Set an explicit DATABASE_URL=file:./e2e.db only when you intentionally
need the legacy SQLite E2E path. Set E2E_REUSE_SERVER=1 only when you are not
resetting the database between runs.
all includes TypeScript, Python, and CLI quality checks plus dependency cooldown
validation for pnpm and uv support.
full also runs the Trivy supply-chain audit gate before production dependency
audits and E2E tests.
Quality checks can also be run independently:
pnpm run quality:ts
pnpm run quality:python
pnpm run quality:cliquality:ts runs blocking ESLint complexity, SonarJS cognitive-complexity, and
jscpd duplication thresholds plus dependency-cruiser import-cycle analysis.
quality:python runs Ruff plus blocking Xenon and complexipy complexity
thresholds with Radon reporting for the worker package.
quality:cli runs Go formatting checks, go vet, Staticcheck, a blocking
gocyclo complexity threshold, go test, and go build for the CLI package.
Complexity thresholds are set to the current repo baseline and block regressions by default:
- TypeScript cyclomatic complexity: 56
- TypeScript cognitive complexity: 24
- Python Xenon: max absolute F, max module C, max average B
- Python Radon cyclomatic complexity: 44
- Python complexipy cognitive complexity: 46
- CLI Go cyclomatic complexity: 15
- jscpd duplication: 3.1%
To bypass only the numeric threshold blockers during an intentional transition,
set QUALITY_THRESHOLDS_BYPASS=1. Formatting, lint correctness, tests, security
scans, and other non-threshold checks still run normally.
The Go CLI lives in cli/ and has its own guide at cli/README.md.
For day-to-day CLI usage, see the user guide at docs/cli-user-guide.md.
It now starts with a short copy/paste CLI cheat sheet for the most common workflows.
That guide covers:
- building
starterctl - local PAT-based configuration
- smoke testing against
http://localhost:3270 - manual cross-platform builds
- GoReleaser snapshot packaging
- Local
pnpm run devuses SQLite viaDATABASE_URL=file:./dev.db. - Put Docker-only PostgreSQL and initial admin values in
.env.dockerusing.env.docker.exampleas the template, then start Docker withpnpm docker up. - Production-style Docker uses separate database URL names by runtime:
APP_DATABASE_URL,WORKER_DATABASE_URL, andMIGRATION_DATABASE_URL. - Docker services receive explicit allowlisted environment variables; the Postgres container only receives
POSTGRES_DB,POSTGRES_USER, andPOSTGRES_PASSWORD. pnpm docker build app migrate workerbuilds the production app, migration, and worker images.pnpm docker up -d workerstarts the Python background worker against the same Postgres database.- Review
docs/runtime-credentials.mdbefore adding secrets to app, worker, or migration environments.
- The current mail abstraction lives under
src/lib/mailand currently supports thegraphprovider only. - The first implementation supports reading mailbox messages and sending mail through Microsoft Graph with application credentials.
- Configure
GRAPH_CLIENT_ID,GRAPH_CLIENT_SECRET,GRAPH_TENANT_ID,MAIL_PROVIDER=graph, andMAIL_DEFAULT_MAILBOXin your environment. - For shared mailboxes, assign the needed Graph mail permissions to the Entra app and scope access appropriately in Exchange.
- See
docs/mail.mdfor the current abstraction shape, environment variables, permissions, and sample usage.
- Request IDs are assigned in middleware and returned as the
x-request-idresponse header. - Structured JSON logs are emitted through
src/lib/logger.tsand redact common secrets automatically. Seedocs/logging.mdfor event naming, safe metadata, worker logging, and guardrail rules. - Runtime process failures are captured in
src/instrumentation.ts. - Health checks are exposed at
/api/healthwith process and database status. - Build metadata is exposed at
/api/versionand shown in the app badge fromAPP_ENVIRONMENT,APP_VERSION,APP_REVISION,APP_BUILD_ID, andAPP_BUILT_AT; CI/deploys should set these from the commit, run id, and release/tag instead of committing generated version files. LOG_LEVELcontrols severity filtering.ENABLE_REQUEST_LOGGING=trueenables opt-in request completion logs.
- pnpm is configured with a 7-day package release delay through
.npmrc - uv worker resolution is configured with
exclude-newer = "1 week" validate.ps1fails if the installed pnpm or uv version does not support those controls- Trivy runs from a repo-pinned digest image and must satisfy the same 7-day
scanner release cooldown. Override only with
-AllowTrivyCooldownOverridefor an approved emergency. - Trivy runtime image scans block fixable High/Critical vulnerabilities; unfixed distro findings are ignored by the gate until an upstream fix exists.
Run the Trivy gate directly when you only need image/IaC supply-chain checks:
pnpm run supply-chain:auditReports are written under .artifacts/supply-chain-audit/.
Clone the standalone template repo and continue product-specific work from there instead of from the original worktree.