Boilerplate Readiness
This page enumerates the verification gates that protect the boilerplate's
"safe to fork and build on" contract. The default CI lane is asserted by
.gitea/workflows/ci.yml; heavier gates such as coverage, Lighthouse,
Storybook, a11y, and deploy verification may live in dedicated workflow files.
Failing a required gate blocks the PR or deployment lane that owns it — there
is no silent override mode.
How to read this page
Each section names a gate, what it enforces, the canonical command, and the
file(s) that contain the contract. Gates marked anti-regression keep the
current floor; they only allow improvements, never regressions. Gates marked
hard contract assert structural invariants that must always hold.
Onboarding & branding
| Gate |
Contract |
Command |
| Brand env layer (hard) |
All user-facing brand strings are overridable via BRAND_* env vars in packages/config/src/catalog.ts. packages/brand/src/env.ts is the source-of-truth loader. |
pnpm --filter @toolctrl/brand test |
| Infra-host parametrization (hard) |
Gitea runner label, registry host, image prefix, DB user/name read from scripts/infra-config.mjs with TOOLCTRL_* env overrides — forks can sed-replace strings and set vars without rewriting verify-gitea-*-workflow.mjs. |
pnpm run verify:gitea-ci-workflow && pnpm run verify:deploy-workflow |
.env.example drift (hard) |
Every key in .env.{example,local.example,production.example,fly.example,test} exists in the catalog. |
pnpm exec vitest run packages/config/src/env-examples.test.ts |
Internationalization
| Gate |
Contract |
Command |
| DE/EN namespace parity (hard) |
All registered i18nNamespaces stay in lockstep across de and en, including app, auth, billing, CRM, settings, email marketing, mail, and error namespaces. |
pnpm run verify:i18n |
| Hardcoded user-facing strings (anti-regression) |
brand-literal, jsx-text, user-facing-prop-literal, and user-facing-response-literal counts may only decrease over the ratcheted baseline; current zero-floor categories must stay at zero. |
node scripts/verify-hardcoded-strings.mjs |
| RTL/direction (hard) |
getDirection(locale) covers known RTL languages; isRTL matches. |
pnpm --filter @toolctrl/i18n test |
| Mail templates (hard) |
renderTransactionalEmail(template, props) (React-Email) renders subject + body for every supported template. Host-app translator hooks are reserved for a future i18n pass; templates emit English copy directly. |
pnpm --filter @toolctrl/mail test |
Security
| Gate |
Contract |
Command |
| Secret scanning (hard) |
10 secret-shaped patterns (GitHub PAT, AWS, Stripe sk_live_*, Slack, PEM, JWT, generic) scanned across apps/packages/scripts/prisma/docs/. Allowlist for .env.example variants and test fixtures. |
pnpm run verify:secrets |
| CSRF inventory (hard) |
Every mutating POST/PATCH/DELETE route is classified in docs/security/csrf-inventory.md. |
pnpm exec vitest run apps/api/src/csrf-inventory.test.ts apps/web/app/utils/session-csrf-sweep.test.ts |
| Route access inventory (hard) |
Every API route has an access class (tenant-api-key, service-token, public-health, …) documented + matching OpenAPI security. |
pnpm run verify:api-inventory && pnpm run verify:openapi-security |
| Tenant isolation (hard) |
1115 lines of runtime tests in apps/api/src/isolation/ + apps/web/app/test/isolation/ exercise cross-tenant mutations and assert 403/401. |
pnpm exec vitest run apps/api/src/isolation apps/web/app/test/isolation |
| Permission matrix (hard) |
Role/permission seed is the source-of-truth, docs and tests stay aligned. |
pnpm run verify:permission-matrix |
| Auth error envelope (hard) |
Auth middleware throws { error: { code, messageKey } } envelopes, never free strings. |
pnpm exec vitest run apps/web/app/utils/loaders.middleware.test.ts |
| STRIDE threat model (doc) |
docs/security/threat-model.md lists Spoofing/Tampering/Repudiation/Information-disclosure/DoS/Elevation threats and links each to a gate. |
(review) |
Quality & code hygiene
| Gate |
Contract |
Command |
| Phase-C boundaries (hard) |
UI packages cannot import @toolctrl/db; runtime separation enforced. |
pnpm run verify:phase-c-boundaries |
| ESLint baseline (anti-regression) |
eslint-disable, @ts-ignore, @ts-expect-error counts only decrease from the ratcheted baseline. |
pnpm run verify:eslint-baseline |
| Test density (anti-regression) |
12 workspace dirs maintain a minimum test-file-per-source-file ratio. |
pnpm run verify:test-density |
| Test coverage (anti-regression) |
scripts/coverage-targets.json defines active per-path thresholds and release-floor sprint targets; @vitest/coverage-v8 emits coverage-summary.json for the gate. The latest backlog has no critical-path positive gaps. |
pnpm run verify:test-coverage |
| Hot-swap mock visual (hard) |
verify:ui-visual + test:hot-swap keep mock-driven surface stable. |
pnpm run verify:ui-visual && pnpm run test:hot-swap |
Operations & runtime
| Gate |
Contract |
Command |
| Docker non-root (hard) |
All runtime stages (web, api, worker) drop to USER node before CMD/ENTRYPOINT. |
pnpm run verify:docker-non-root |
| Docker baseline (hard) |
Dockerfile stage layout + healthchecks per stage stable. |
pnpm run verify:docker-baseline |
| Bundle-size budget (anti-regression) |
JS chunks (root/route/vendor/other) and CSS stay under the +20% headroom budget from current state. |
pnpm run verify:bundle-size |
| Production seed artifact (hard) |
The compiled production seed bundle is present and idempotent. |
pnpm run verify:production-seed-artifact |
| Migration rollback drill (manual) |
scripts/drill-migration-rollback.sh replays forward chain + seed against an ephemeral postgres and asserts a re-apply is a no-op. |
./scripts/drill-migration-rollback.sh |
| Health baseline (hard) |
/healthz and /readyz shapes are stable. |
pnpm run verify:health-baseline |
Supply chain
| Mechanism |
Coverage |
File |
| Dependabot |
weekly npm (grouped: ai-providers / react-stack / test / lint), weekly github-actions, monthly docker |
.github/dependabot.yml |
| Renovate (Gitea) |
same group config for self-hosted Gitea forks |
.gitea/renovate.json |
| SBOM |
scripts/generate-sbom.mjs writes sbom-pnpm.json during the build stage; every runtime image carries the SBOM at /myapp/sbom-pnpm.json and labels it via org.opencontainers.image.sbom. |
Dockerfile, scripts/generate-sbom.mjs |
| Production seed bootstrap |
SEED_ADMIN_EMAIL/SEED_ADMIN_PASSWORD required for prod seed; rejects the local default. |
packages/db/src/production-seed.server.ts |
Documentation
| Doc |
Purpose |
docs/quickstart.md |
Install-to-first-local-run path |
docs/new-project.md |
Adapting the boilerplate to a new project |
docs/deployment-porting.md |
Gitea infra-string fork procedure |
docs/security/threat-model.md |
STRIDE per subsystem with mitigation links |
docs/operations/database-rollback.md |
Forward-only rollback runbook + drill script reference |
docs/ui-inventory/hot-swap-status.md |
UI registry activation state |
Optional gates (opt-in)
Some verification surfaces require a deployed environment or an external
binary and are therefore not wired into the default CI lane. They SKIP
cleanly (exit 0) when their prerequisites are missing, so they are safe to
run locally or to enable in a downstream pipeline.
| Gate |
Contract |
Command |
Prerequisites |
| Load tests (opt-in) |
k6 scripts in tests/load/ exercise login, tenant-scoped read, and tenant-scoped mutating paths against a deployed target. Thresholds: http_req_failed: rate<0.05, login p(95)<400ms / p(99)<800ms, read p(99)<300ms and >200 req/s, mutating p(99)<600ms. |
pnpm run verify:load |
k6 binary on PATH (brew install k6 or https://k6.io/docs/getting-started/installation/); reachable K6_TARGET_URL (default http://localhost:3000); LOAD_TENANT_API_KEY for the read/mutating scripts. See tests/load/README.md for the full env contract. |
To activate the load suite in a release lane, set K6_TARGET_URL and
LOAD_TENANT_API_KEY for that lane and add pnpm run verify:load to the
job. The runner SKIPs when k6 is missing or the target is offline — the
gate never blocks a CI run on environments that cannot satisfy its
prerequisites.
Composite "all gates" run
There is no single mega-script — gates are intentionally separate so CI can
parallelise. The canonical CI order is asserted by
verify:gitea-ci-workflow against .gitea/workflows/ci.yml. To dry-run the
local subset:
Any failure here is a CI failure. Anti-regression gates lock the current
ratchet; hard contracts assert immutable structural invariants. Adopt and
extend gates rather than disable them.