15 KiB
ToolCTRL Boilerplate Plan
Status: Historical planning artifact (pre-Architecture Closure). This document captures the original target direction. Many sections have been implemented or superseded by later milestones (Architecture Closure, Auth API, Body-Release Docs baselines). Use the following sources for current state instead:
docs/architecture-migration-status.md— boundary status and intentional deferralsdocs/feature-inventory.md— core/plugin/remove classification per featuredocs/archive/architecture-handoff-summary.md— official Architecture Closure recorddocs/feature-manifests/*.json— machine-readable feature contractsdocs/agent-entrypoints.md— recommended entry point for substantial feature workRefer to this file only to recover original intent or rationale not preserved elsewhere.
This document defines the target direction for the ToolCTRL standard boilerplate. It is intentionally a planning artifact first: implementation starts only after the relevant section has been discussed and accepted.
Principles
- Build a whitelabel boilerplate, not a lightly edited source template fork.
- Prefer explicit deployable boundaries over a large all-in-one app.
- Keep changes atomic and commit each completed step separately.
- Make i18n mandatory for user-facing UI. German is the default language.
- Keep provider integrations optional unless ToolCTRL explicitly standardizes them.
- Optimize for replacing implementation details later, especially UI components and external providers.
Package Manager
Decision: move to pnpm and use pnpm workspaces.
Rationale:
- Workspaces are first-class and predictable for monorepo packages.
- Dependency installation is faster and less duplicated.
- Package boundaries become easier to enforce once
apps/*andpackages/*exist. - CI and Docker can install deterministically with
pnpm-lock.yaml.
Planned migration:
- Add
packageManagertopackage.json. - Add
pnpm-workspace.yaml. - Generate
pnpm-lock.yaml. - Remove
package-lock.json. - Update README, Dockerfile, CI and local commands from
npmtopnpm. - Verify with
pnpm install,pnpm typecheck,pnpm test, andpnpm build.
Open question:
- Should we pin via Corepack only, or also document a global
pnpminstall fallback?
Target Monorepo
Target structure:
apps/
web/
api/
worker/
packages/
auth/
config/
db/
i18n/
mail/
queue/
shared/
ui/
Historical note: this was the intended structure before Architecture Closure.
Use docs/architecture-migration-status.md for the current workspace/package
snapshot before planning moves or imports.
Initial interpretation:
apps/web: React Router app for authenticated app UI.apps/api: versioned API server atapi.<domain>/v1.apps/worker: BullMQ workers and scheduled/background jobs.packages/db: Prisma schema/client initially, with ORM decision isolated later.packages/config: typed env/config loading for web, api and worker.packages/auth: session/auth helpers shared by web and api where appropriate.packages/i18n: locale config, translation loading and key conventions.packages/mail: MailerSend default provider and mail abstractions.packages/queue: BullMQ queue names, producers and payload schemas.packages/ui: replaceable UI adapter layer.packages/shared: cross-cutting types and utilities that are not app-specific.
Migration approach:
- Introduce workspaces and package boundaries.
- Move code only when the target ownership is clear.
- Avoid massive path churn before config, API and worker boundaries are agreed.
- Keep the current app working between phases.
Current-state contract: docs/architecture-migration-status.md is the active workspace/package snapshot, and docs/feature-inventory.md plus docs/feature-manifests/*.json classify features as keep, plugin-candidate or remove-candidate. Implementation phases should use those documents as the pruning contract and cleanly remove or extract replaced features instead of adding permanent legacy adapters.
Domains And Routing
Target domains:
- Public/marketing site: out of this boilerplate.
- Web app:
app.<domain>. - Admin app:
admin.<domain>. - API:
api.<domain>/v1.
Tenant routing:
- Current standard
/app/:tenant/...should be replaced. - Target app tenant route:
https://app.<domain>/<tenant_slug>/.... - Admin should not be tenant-prefixed by default unless a specific admin workflow requires tenant context.
Planned routing migration:
- Add host-aware routing/config helpers.
- Introduce canonical URL builders for app/admin/api.
- Replace hardcoded
/app/:tenantgeneration. - Add redirects from old
/app/:tenant/...to new/<tenant>/...where useful. - Ensure auth redirects understand host and tenant slug.
Open questions:
- Should
admin.<domain>allow tenant-scoped admin pages via query/context, or use paths like/tenants/:tenant? - Should local development use
localhostpaths only, or local hostnames such asapp.localhostandapi.localhost?
API
Recommendation: use Hono for apps/api.
Rationale:
- Small, explicit and fast.
- Works well as a dedicated Node/Docker API.
- Clean versioning:
/v1. - Pairs well with Zod request/response schemas and OpenAPI generation.
- Avoids dragging the web framework into API concerns.
Second choice: Fastify, if we prefer a more traditional Node server with a larger plugin ecosystem.
Initial API target:
GET /v1/health- auth/session validation primitives
- account/tenant/user endpoints currently living under web routes
- public API key validation and entity endpoints
Planning rules:
- No API extraction until route ownership is mapped.
- New API endpoints should use typed schemas.
- Web should consume API only where it makes architectural sense; server-rendered web loaders can still use packages directly if that is the agreed boundary.
Open questions:
- Do we want OpenAPI as a required artifact for every endpoint?
- Should internal web-to-api calls use HTTP in production, or should web import package functions directly for trusted server-side paths?
Worker And Queues
Decision basis: Redis/BullMQ and Docker, matching the HiveGrid operating model.
Target:
apps/workerruns on a dedicated server or container.- Redis is the queue backend.
- Queue definitions live in
packages/queue. - Job handlers live either in
apps/workeror domain packages, depending on ownership.
Candidate queues:
mailnewsletterwebhooksimportsexportsnotificationsanalyticsaicleanup
Planned worker discussion:
- Deployment model: Docker Compose, single worker image, or multiple worker processes.
- Redis topology: shared Redis, dedicated Redis, persistence expectations.
- Retry policy and dead-letter handling.
- Job payload versioning.
- Admin/observability UI for queues.
- Scheduling strategy for cron-like work.
Open questions:
- Do we want Bull Board or another queue dashboard?
- Do we split worker processes per queue or run one worker with configurable queues?
- What is the standard production Docker Compose layout?
Environment And Config
Goal: replace the current broad .env surface with typed, app-specific configuration.
Target files:
.env.example.env.local.example.env.production.example- optional app-specific sections for web, api and worker
Target package:
packages/config
Classification for every variable:
- required core
- required per app
- optional provider
- local development only
- production only
- deprecated/remove
Planned audit:
- List every current env variable.
- Identify which code path consumes it.
- Decide keep/remove/provider-optional.
- Rename variables to stable ToolCTRL naming.
- Add typed validation.
- Update Docker and CI examples.
Decision: MailerSend is the default provider.
Scope:
- Transactional email.
- Newsletter/bulk email where MailerSend supports the required workflow cleanly.
Target package:
packages/mail
Planned migration:
- Define mail provider interface.
- Implement MailerSend adapter.
- Move existing templates behind the interface.
- Remove legacy transactional provider defaults and adapters.
- Keep MailerSend as the only transactional runtime provider.
- Replace env variables and admin settings.
Open questions:
- Do newsletters go through MailerSend directly, or do we keep a CRM/list abstraction?
- Should all mail be queued through BullMQ by default?
- Do we need per-tenant sender identities in the boilerplate?
i18n
Decision: German is the default language. i18n is mandatory.
Target:
- Add
delocale. - Make
dedefault. - No new user-facing hardcoded strings.
- Language setup lives in
packages/i18nafter monorepo migration.
Planned migration:
- Add German locale file.
- Switch default locale to German.
- Audit missing translation keys.
- Add guidance for new UI copy.
- Remove stale languages if not needed.
Open questions:
- Keep English as fallback or German as both default and fallback?
- Do we keep Spanish/French from the source app?
Affiliate System
Decision: remove affiliate/rewardful as standard boilerplate functionality.
Scope:
- Routes.
- Sidebar links and commands.
- Rewardful scripts.
- Env vars.
- App configuration fields.
- Pricing/admin UI references.
- Tests and docs.
Potential future:
- Affiliate support may return later as an optional package/plugin, not as default boilerplate code.
Whitelabel Cleanup
Goal: remove all source-product references and source-product assumptions.
Scope:
- Package metadata.
- README/docs.
- Legal copy.
- Email templates.
- UI copy.
- Images/logos.
- Surveys/fake data.
- Changelog/default data.
- External links.
- Env examples.
- Comments that imply source-product ownership.
Target naming:
- Project/product: ToolCTRL Boilerplate where a concrete name is needed.
- Generic output: application, workspace, tenant, organization, account.
Marketing Removal
Decision: marketing is not part of this boilerplate.
Implication:
- Root domain belongs to a separate marketing site/project.
- This repo should focus on app, admin, API and worker.
What stays for now:
- Minimal legal/auth pages if needed for app flows.
- Header/footer UI primitives only if still used by app pages.
What should be removed or made optional:
- Landing-page-oriented routes.
- Changelog/newsletter/contact public marketing surfaces, unless kept as app support features.
- Marketing-specific page block examples.
Open question:
- Should
/contactand/newsletterbe removed entirely, or converted into authenticated support/admin features?
Components And UI Adapter
Goal: make the component library hotswappable later.
Target:
packages/uibecomes the import boundary for application UI primitives.- App code should not import randomly from mixed component folders long term.
- Domain components live near domains, primitives live in
packages/ui.
Planned consolidation:
- Inventory current UI imports.
- Define primitive categories: buttons, inputs, dialogs, tables, layout, navigation, feedback.
- Create adapter exports.
- Migrate app code gradually to adapter imports.
- Remove duplicate components.
- Add rules for future components.
Open questions:
- Do we want shadcn/Radix as the first adapter implementation?
- Should
packages/uipublishable/private package conventions be used from day one?
ORM Strategy
Current state:
prisma:6.18.0@prisma/client:6.18.0
Decision: do not combine ORM migration with monorepo/API/worker split.
Recommended sequence:
- Keep Prisma during structural migration.
- Move Prisma ownership to
packages/db. - After boundaries are stable, run a dedicated ORM decision phase:
- Prisma 7 upgrade
- Drizzle migration
- stay on Prisma 6 temporarily
Prisma 7 consideration:
- Major upgrade should be isolated.
- Expect config/client generation/schema changes to touch build and deployment.
- Worth evaluating once
packages/dbexists.
Drizzle consideration:
- Pros: SQL-nearer, lightweight, easier explicit queries, attractive for package boundaries.
- Cons: large migration cost from current Prisma-heavy codebase, rewrite of generated types and query layer.
Recommendation for now:
- Use Prisma while splitting.
- Revisit Prisma 7 vs Drizzle only after
packages/dbexists and the API/worker boundary is clear.
Quality And Performance
Target areas:
- Dependency cleanup.
- Bundle size.
- Server loader performance.
- Database query count and indexes.
- Auth/session overhead.
- Analytics overhead.
- Background job offloading.
- Docker build speed.
- Test speed and coverage around routing/auth/config.
Planned approach:
- No broad performance rewrite before architecture boundaries.
- Add measurements where possible.
- Convert slow or long-running work into queued jobs.
Proposed Phase Order
Phase 1: Planning And Tooling
- Commit this plan.
- Switch to pnpm workspaces.
- Update commands, lockfile, Docker and CI.
- Verify current app still works.
Phase 2: Config Audit
- Inventory
.env. - Define typed config package.
- Remove unused service variables.
- Document standard local/prod env files.
Phase 3: Monorepo Skeleton
- Add
apps/*andpackages/*structure. - Move only low-risk shared modules first.
- Keep app running.
Phase 4: Domain And Routing Plan Implementation
- Introduce host-aware URL helpers.
- Support
app.<domain>/<tenant>. - Introduce
admin.<domain>. - Plan redirects from legacy
/app/:tenant.
Phase 5: API Split
- Add Hono-based
apps/api. - Add
/v1/health. - Move selected API routes behind schemas.
- Add API Docker target.
Phase 6: Worker Split
- Add
apps/worker. - Add BullMQ/Redis configuration.
- Add queue package.
- Move mail/webhook jobs first.
Phase 7: MailerSend
- Add MailerSend provider.
- Queue mail by default if agreed.
- Remove legacy transactional provider defaults and adapters.
Phase 8: i18n German Default
- Add German locale.
- Make German default.
- Audit missing/hardcoded strings.
Phase 9: Remove Affiliate And Marketing
- Remove affiliate/rewardful.
- Remove marketing routes and config.
- Keep only app-required public pages.
Phase 10: Whitelabel Cleanup
- Remove source-product mentions.
- Replace source copy/assets/default data.
- Update docs and examples.
Phase 11: UI Consolidation
- Introduce
packages/uias adapter. - Migrate imports gradually.
- Remove duplicate primitives.
Phase 12: ORM Decision
- Evaluate Prisma 7 upgrade vs Drizzle migration.
- Decide based on the stable package boundaries.
- Execute as isolated phase.
- Foundation note:
packages/db/ORM-DECISION.mdpreserves Prisma for now and defines later Prisma 7 vs Drizzle criteria.
Phase 13: QoL And Performance
- Dependency cleanup.
- Bundle and server performance.
- Query optimization.
- Developer workflow polish.
- Foundation note:
docs/qol-performance-backlog.mdtracks verified warnings for bundle chunks, Prisma 7 deprecation, sourcemaps, lint config and Docker platform.
Immediate Next Decisions
Before implementation beyond this plan:
- Confirm local dev domain strategy: plain localhost paths or
app.localhost/api.localhost. - MailerSend-only transactional mail is the migration target; legacy provider defaults and adapters are removed rather than retained.
- Public
/contactand/newsletterwere removed as default marketing surfaces. - Confirm whether Spanish/French locales should stay after German is added.
- Confirm worker dashboard preference.