PMTL_VN Architecture
Overview
Use this skill to keep work aligned with the real PMTL_VN architecture. It is for building new features, reviewing/refactoring code, evolving auth and access control, wiring search, and maintaining Docker/Caddy deployment without breaking monorepo boundaries or file conventions.
Use When
- Creating, extending, or refactoring PMTL features across app boundaries.
- Changing auth, access control, search, deployment, or runtime service ownership.
- Reviewing placement decisions when multiple layers could own the work.
Expected Output
- Code placed in the narrowest correct layer.
- Architecture changes documented in the same task when contracts move.
The current stack assumptions are:
apps/web is Next.js 16 App Router
apps/api is NestJS backend authority (business logic, auth, OpenAPI)
apps/admin is custom admin frontend
- PostgreSQL is the primary database (source of truth)
- Phase 1: SQL/API search (tsvector/ILIKE); Phase 2+: Meilisearch
- Caddy terminates SSL and reverse proxies services
- Docker Compose is the operational boundary
- Phase 2+: Valkey, BullMQ workers, outbox_events — only when measured need
Core Rules
Follow these rules on every task:
- Preserve the monorepo split:
apps/web, apps/api, apps/admin, packages/*, infra/*, docs/*
- Keep web feature-first
- Keep NestJS modules clean: module + controller + service + dto + entities
- Keep
packages/shared framework-agnostic: types, enums, zod schemas, constants, validators, mappers, pure utils only
- Do not move business logic into Next route/page files or admin configs
- Introduce Valkey, BullMQ, workers only when there is a concrete production need, and document the runtime contract
- In
apps/web, prefer Next.js 16 cacheComponents with "use cache" helper functions over route-level revalidate or unstable_cache
- In
apps/web, request-boundary logic belongs in src/proxy.ts for Next.js 16; do not blindly migrate to middleware.ts
- Prefer clear, maintainable code over clever abstractions
- When audit documents disagree, treat
AUDIT_VERIFIED_2026.md as the checked baseline and older audits as hypotheses to validate
- Update docs and env examples when architecture, contracts, or runtime requirements change
- If you change project rules or AI-coding conventions, update the corresponding docs and skill files in the same task
Repo Shape
Assume this structure unless the user explicitly changes it:
apps/web: Next.js 16 frontend
apps/api: NestJS backend authority
apps/admin: Custom admin frontend
packages/ui: optional shared UI
packages/shared: pure shared domain code
packages/config: shared eslint/typescript/prettier config
infra/docker: compose files and env examples
infra/caddy: Caddyfile and related files
infra/scripts: deploy and backup scripts
docs/architecture: decisions, domains, conventions, deployment
docs/api: contracts
design/: architecture design source of truth
Consult references/repo-conventions.md for placement and layering rules.
Task Routing
When working on web
Use apps/web and keep code feature-first.
Typical placement:
- pages/routes/layouts/loading/error/meta in
src/app
- feature code in
src/features/<domain>
- cross-feature web helpers in
src/lib
- shared visual primitives in
src/components
For auth in web:
- prefer
features/auth/*
- add route protection in
proxy.ts or the least invasive server-side guard that fits the existing setup
- treat
apps/api as the auth authority
When working on api (NestJS)
Use apps/api and keep modules focused and clean.
Per module/domain, prefer:
{module-name}.module.ts: NestJS module declaration
{module-name}.controller.ts: route handlers
{module-name}.service.ts: business logic
dto/: input/output DTOs with Zod validation
entities/: Prisma entity types
guards/: route guards if module-specific
Platform modules go in apps/api/src/platform/:
- sessions, audit, feature flags, rate limit, storage, health, metrics
When working on shared domain code
Use packages/shared only for framework-agnostic code:
constants
enums
schemas
types
validators
mappers
- pure
utils
Do not import Next.js internals, NestJS internals, or runtime-only server utilities here.
When working on infra or deployment
Use:
infra/docker for compose files and env examples
infra/caddy for reverse proxy and SSL config
infra/scripts for deployment and DB backup scripts
- GitHub Actions for build/push/deploy automation
Prefer the production model:
- build images locally or in CI
- push to registry
- VPS only pulls and runs containers
Auth Strategy
NestJS (apps/api) is the auth authority using session-based auth with Argon2id password hashing.
Required assumptions:
- auth lives in
apps/api/src/modules/auth/
- web calls API for auth operations
- session strategy uses httpOnly cookies with refresh token rotation
- do not add Auth.js, Better Auth, or a second auth authority unless the user explicitly asks for it
Typical auth scope:
- register
- login
- logout / logout-all
- forgot password
- reset password
- current user / session
- role-based access
- protected routes
- user profile basics
Default roles:
Consult design/01-identity/ for auth and identity design.
Search Strategy
Phase 1 (current): SQL/API search using PostgreSQL tsvector or ILIKE.
Phase 2+: Meilisearch is the preferred search engine when search.meilisearch.enabled feature flag is on.
Keep search concerns separated:
- search service in
apps/api/src/modules/search/
- search UI and query composition in web features
- shared search DTOs or schemas in
packages/shared
Do not expose Meilisearch publicly unless explicitly intended. Prefer internal network access behind API services.
Phase Model
Phase 1
Supported and preferred:
- Next.js 16 web
- NestJS backend authority (
apps/api)
- PostgreSQL
- SQL/API search (tsvector/ILIKE)
- Caddy
- Docker Compose dev/prod
- Session-based auth via NestJS
- docs/conventions/contracts maintenance
Phase 2+ (Operational Expansion)
Add when the production runtime benefits clearly justify the added complexity:
- Meilisearch (search.meilisearch.enabled)
- Valkey (cache)
- BullMQ worker service
- outbox_events + dispatcher
- monitoring dashboards
- alerting
- log aggregation
- backup automation beyond simple scripts
Security Baseline
Always preserve these defaults:
- least privilege access control
- guest access only to public content
- protect admin/moderation areas by role
- keep secrets in env, not code
- keep Postgres and Valkey internal-only unless explicitly exposed
- keep Meilisearch internal-only unless explicitly exposed
- use Caddy for TLS termination and reverse proxy
- add limits and guardrails to sensitive routes when implementing auth, comments, abuse reporting, or search
Consult design/baseline/ for security and infrastructure design.
Documentation Duties
When you change behavior or architecture, update docs as part of the same task when appropriate:
README.md for run/build/deploy changes
design/ for architecture design changes
docs/architecture/ for implementation conventions
docs/api/ for request/response or auth contract changes
.env.example or env example files for runtime configuration changes
Verification
- Recheck the chosen file placement against
references/repo-conventions.md.
- If service boundaries or public/private exposure changed, recheck the relevant security and deployment references.
- Pair with
pmtl-verify-quality-gate after meaningful implementation changes.
Recommended Working Style
When given a task:
- Read the architecture docs the repo points to first.
- Identify the domain and the correct layer.
- Place new code in the narrowest correct location.
- Keep NestJS modules clean: thin controllers, business logic in services.
- Keep feature code grouped by domain in web.
- Preserve type safety without over-engineering.
- Explain file placement and architectural choices briefly when useful.
- Do not introduce broad refactors unless they are required.
Common Good Decisions
- Put auth UI and fetch helpers under
apps/web/src/features/auth
- Put ownership/moderation logic in NestJS services, not only controller guards
- Put DTO validation in
packages/shared/src/schemas
- Put search logic in
apps/api/src/modules/search/
- Put deploy and backup logic under
infra/scripts
- Put Docker/Caddy changes under
infra/* and document them
Common Bad Decisions
- Mixing framework internals into
packages/shared
- Putting all module logic inside a single controller file
- Adding Valkey/BullMQ without a concrete runtime use case or deployment plan
- Adding a second auth system without a clear reason
- Building production images directly on the VPS by default
- Exposing internal services publicly without an explicit need
References
- For repo layout and file placement, read
references/repo-conventions.md
- For auth, access, and security defaults, read
references/auth-and-security.md
- For Docker, Caddy, deployment, and service boundaries, read
references/deploy-and-ops.md
- For architecture design source of truth, read
design/DECISIONS.md
1---2name: pmtl-vn-architecture3description: architecture and implementation guide for the pmtl_vn stack using next.js 16, nestjs, postgres, caddy, docker compose, and session-based auth. use when creating, extending, reviewing, refactoring, or deploying this monorepo; when adding auth, roles, access control, search, docker/caddy setup, or domain features; and when the agent must preserve the repo's ai-friendly conventions, boundaries, and file placement rules.4---5
6# PMTL_VN Architecture
7
8## Overview
9Use this skill to keep work aligned with the real PMTL_VN architecture. It is for building new features, reviewing/refactoring code, evolving auth and access control, wiring search, and maintaining Docker/Caddy deployment without breaking monorepo boundaries or file conventions.
10
11## Use When
12
13- Creating, extending, or refactoring PMTL features across app boundaries.
14- Changing auth, access control, search, deployment, or runtime service ownership.
15- Reviewing placement decisions when multiple layers could own the work.
16
17## Expected Output
18
19- Code placed in the narrowest correct layer.
20- Architecture changes documented in the same task when contracts move.
21
22The current stack assumptions are:
23- `apps/web` is Next.js 16 App Router
24- `apps/api` is NestJS backend authority (business logic, auth, OpenAPI)
25- `apps/admin` is custom admin frontend
26- PostgreSQL is the primary database (source of truth)
27- **Phase 1**: SQL/API search (tsvector/ILIKE); **Phase 2+**: Meilisearch
28- Caddy terminates SSL and reverse proxies services
29- Docker Compose is the operational boundary
30- **Phase 2+**: Valkey, BullMQ workers, outbox_events — only when measured need
31
32## Core Rules
33Follow these rules on every task:
34- Preserve the monorepo split: `apps/web`, `apps/api`, `apps/admin`, `packages/*`, `infra/*`, `docs/*`
35- Keep web feature-first
36- Keep NestJS modules clean: module + controller + service + dto + entities
37- Keep `packages/shared` framework-agnostic: types, enums, zod schemas, constants, validators, mappers, pure utils only
38- Do not move business logic into Next route/page files or admin configs
39- Introduce Valkey, BullMQ, workers only when there is a concrete production need, and document the runtime contract
40- In `apps/web`, prefer Next.js 16 `cacheComponents` with `"use cache"` helper functions over route-level `revalidate` or `unstable_cache`
41- In `apps/web`, request-boundary logic belongs in `src/proxy.ts` for Next.js 16; do not blindly migrate to `middleware.ts`
42- Prefer clear, maintainable code over clever abstractions
43- When audit documents disagree, treat `AUDIT_VERIFIED_2026.md` as the checked baseline and older audits as hypotheses to validate
44- Update docs and env examples when architecture, contracts, or runtime requirements change
45- If you change project rules or AI-coding conventions, update the corresponding docs and skill files in the same task
46
47## Repo Shape
48Assume this structure unless the user explicitly changes it:
49- `apps/web`: Next.js 16 frontend
50- `apps/api`: NestJS backend authority
51- `apps/admin`: Custom admin frontend
52- `packages/ui`: optional shared UI
53- `packages/shared`: pure shared domain code
54- `packages/config`: shared eslint/typescript/prettier config
55- `infra/docker`: compose files and env examples
56- `infra/caddy`: Caddyfile and related files
57- `infra/scripts`: deploy and backup scripts
58- `docs/architecture`: decisions, domains, conventions, deployment
59- `docs/api`: contracts
60- `design/`: architecture design source of truth
61
62Consult `references/repo-conventions.md` for placement and layering rules.
63
64## Task Routing
65### When working on web
66Use `apps/web` and keep code feature-first.
67Typical placement:
68- pages/routes/layouts/loading/error/meta in `src/app`
69- feature code in `src/features/<domain>`
70- cross-feature web helpers in `src/lib`
71- shared visual primitives in `src/components`
72
73For auth in web:
74- prefer `features/auth/*`
75- add route protection in `proxy.ts` or the least invasive server-side guard that fits the existing setup
76- treat `apps/api` as the auth authority
77
78### When working on api (NestJS)
79Use `apps/api` and keep modules focused and clean.
80Per module/domain, prefer:
81- `{module-name}.module.ts`: NestJS module declaration
82- `{module-name}.controller.ts`: route handlers
83- `{module-name}.service.ts`: business logic
84- `dto/`: input/output DTOs with Zod validation
85- `entities/`: Prisma entity types
86- `guards/`: route guards if module-specific
87
88Platform modules go in `apps/api/src/platform/`:
89- sessions, audit, feature flags, rate limit, storage, health, metrics
90
91### When working on shared domain code
92Use `packages/shared` only for framework-agnostic code:
93- `constants`
94- `enums`
95- `schemas`
96- `types`
97- `validators`
98- `mappers`
99- pure `utils`
100
101Do not import Next.js internals, NestJS internals, or runtime-only server utilities here.
102
103### When working on infra or deployment
104Use:
105- `infra/docker` for compose files and env examples
106- `infra/caddy` for reverse proxy and SSL config
107- `infra/scripts` for deployment and DB backup scripts
108- GitHub Actions for build/push/deploy automation
109
110Prefer the production model:
111- build images locally or in CI
112- push to registry
113- VPS only pulls and runs containers
114
115## Auth Strategy
116NestJS (`apps/api`) is the auth authority using session-based auth with Argon2id password hashing.
117
118Required assumptions:
119- auth lives in `apps/api/src/modules/auth/`
120- web calls API for auth operations
121- session strategy uses httpOnly cookies with refresh token rotation
122- do not add Auth.js, Better Auth, or a second auth authority unless the user explicitly asks for it
123
124Typical auth scope:
125- register
126- login
127- logout / logout-all
128- forgot password
129- reset password
130- current user / session
131- role-based access
132- protected routes
133- user profile basics
134
135Default roles:
136- `super_admin`
137- `admin`
138- `member`
139
140Consult `design/01-identity/` for auth and identity design.
141
142## Search Strategy
143**Phase 1** (current): SQL/API search using PostgreSQL tsvector or ILIKE.
144**Phase 2+**: Meilisearch is the preferred search engine when `search.meilisearch.enabled` feature flag is on.
145
146Keep search concerns separated:
147- search service in `apps/api/src/modules/search/`
148- search UI and query composition in web features
149- shared search DTOs or schemas in `packages/shared`
150
151Do not expose Meilisearch publicly unless explicitly intended. Prefer internal network access behind API services.
152
153## Phase Model
154### Phase 1
155Supported and preferred:
156- Next.js 16 web
157- NestJS backend authority (`apps/api`)
158- PostgreSQL
159- SQL/API search (tsvector/ILIKE)
160- Caddy
161- Docker Compose dev/prod
162- Session-based auth via NestJS
163- docs/conventions/contracts maintenance
164
165### Phase 2+ (Operational Expansion)
166Add when the production runtime benefits clearly justify the added complexity:
167- Meilisearch (search.meilisearch.enabled)
168- Valkey (cache)
169- BullMQ worker service
170- outbox_events + dispatcher
171- monitoring dashboards
172- alerting
173- log aggregation
174- backup automation beyond simple scripts
175
176## Security Baseline
177Always preserve these defaults:
178- least privilege access control
179- guest access only to public content
180- protect admin/moderation areas by role
181- keep secrets in env, not code
182- keep Postgres and Valkey internal-only unless explicitly exposed
183- keep Meilisearch internal-only unless explicitly exposed
184- use Caddy for TLS termination and reverse proxy
185- add limits and guardrails to sensitive routes when implementing auth, comments, abuse reporting, or search
186
187Consult `design/baseline/` for security and infrastructure design.
188
189## Documentation Duties
190When you change behavior or architecture, update docs as part of the same task when appropriate:
191- `README.md` for run/build/deploy changes
192- `design/` for architecture design changes
193- `docs/architecture/` for implementation conventions
194- `docs/api/` for request/response or auth contract changes
195- `.env.example` or env example files for runtime configuration changes
196
197## Verification
198
199- Recheck the chosen file placement against `references/repo-conventions.md`.
200- If service boundaries or public/private exposure changed, recheck the relevant security and deployment references.
201- Pair with `pmtl-verify-quality-gate` after meaningful implementation changes.
202
203## Recommended Working Style
204When given a task:
2051. Read the architecture docs the repo points to first.
2062. Identify the domain and the correct layer.
2073. Place new code in the narrowest correct location.
2084. Keep NestJS modules clean: thin controllers, business logic in services.
2095. Keep feature code grouped by domain in web.
2106. Preserve type safety without over-engineering.
2117. Explain file placement and architectural choices briefly when useful.
2128. Do not introduce broad refactors unless they are required.
213
214## Common Good Decisions
215- Put auth UI and fetch helpers under `apps/web/src/features/auth`
216- Put ownership/moderation logic in NestJS services, not only controller guards
217- Put DTO validation in `packages/shared/src/schemas`
218- Put search logic in `apps/api/src/modules/search/`
219- Put deploy and backup logic under `infra/scripts`
220- Put Docker/Caddy changes under `infra/*` and document them
221
222## Common Bad Decisions
223- Mixing framework internals into `packages/shared`
224- Putting all module logic inside a single controller file
225- Adding Valkey/BullMQ without a concrete runtime use case or deployment plan
226- Adding a second auth system without a clear reason
227- Building production images directly on the VPS by default
228- Exposing internal services publicly without an explicit need
229
230## References
231- For repo layout and file placement, read `references/repo-conventions.md`
232- For auth, access, and security defaults, read `references/auth-and-security.md`
233- For Docker, Caddy, deployment, and service boundaries, read `references/deploy-and-ops.md`
234- For architecture design source of truth, read `design/DECISIONS.md`