Files
schalli 89f559ce62 docs(03): create phase plan for Module System & Domaincheck
4 plans across 3 waves: SDK + registry (W1), Domaincheck module +
lazy loading (W2), visual verification (W3). Covers MOD-01..04,
DCHK-01..03.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:13:20 +02:00

13 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
03-module-system-domaincheck 02 execute 2
03-01
apps/api/src/domaincheck/domaincheck.module.ts
apps/api/src/domaincheck/domaincheck.service.ts
apps/api/src/domaincheck/domaincheck.controller.ts
apps/api/src/domaincheck/dto/check-domain.dto.ts
apps/api/src/domaincheck/domaincheck.seed.ts
apps/api/src/app.module.ts
apps/web/src/app/(portal)/modules/domaincheck/page.tsx
apps/web/src/app/(portal)/modules/domaincheck/components/DomainInput.tsx
apps/web/src/app/(portal)/modules/domaincheck/components/ResultList.tsx
apps/web/src/app/(portal)/modules/domaincheck/actions.ts
apps/web/messages/de.json
apps/web/messages/en.json
true
DCHK-01
DCHK-02
DCHK-03
truths artifacts key_links
User can open the Domaincheck module and see an input field
User enters a domain name and sees TLD variant results
Results show green (available) or red (registered) status per TLD
path provides exports
apps/api/src/domaincheck/domaincheck.service.ts DNS-based domain availability checking
DomaincheckService
path provides exports
apps/api/src/domaincheck/domaincheck.controller.ts POST /modules/domaincheck/check endpoint
DomaincheckController
path provides
apps/web/src/app/(portal)/modules/domaincheck/page.tsx Domaincheck UI page within portal
from to via pattern
apps/web/src/app/(portal)/modules/domaincheck/actions.ts /modules/domaincheck/check fetch POST to API fetch.*modules/domaincheck/check
from to via pattern
apps/api/src/domaincheck/domaincheck.service.ts node:dns/promises dns.resolve for availability check dns.resolve
Deliver a working Domaincheck module end-to-end — user enters a domain name, backend checks TLD variants via DNS, frontend shows green/red results. This is the proof-of-concept module validating the entire module system.

Purpose: Per D-01, D-02, D-03 — validates the module system with a real, interactive module. Users get immediate value. Output: DomaincheckModule (NestJS), domaincheck frontend page, module seed data.

<execution_context> @/home/vicolab/.claude/gsd-core/workflows/execute-plan.md @/home/vicolab/.claude/gsd-core/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/03-module-system-domaincheck/03-CONTEXT.md @.planning/phases/03-module-system-domaincheck/03-RESEARCH.md @.planning/phases/03-module-system-domaincheck/03-01-SUMMARY.md @apps/api/src/module-registry/module-registry.service.ts @apps/api/src/module-registry/module.guard.ts @apps/web/src/app/(portal)/layout.tsx @apps/web/messages/de.json @apps/web/messages/en.json

Phase Goal

As a user, I want to enter a domain name and see which TLD variants are available or registered, so that I can quickly check domain availability without leaving the platform.

Task 1: Domaincheck Backend — DNS Service + API Endpoint + Module Seed apps/api/src/domaincheck/domaincheck.module.ts, apps/api/src/domaincheck/domaincheck.service.ts, apps/api/src/domaincheck/domaincheck.controller.ts, apps/api/src/domaincheck/dto/check-domain.dto.ts, apps/api/src/domaincheck/domaincheck.seed.ts, apps/api/src/app.module.ts apps/api/src/module-registry/module-registry.service.ts, apps/api/src/module-registry/module.guard.ts, apps/api/src/app.module.ts 1. Create apps/api/src/domaincheck/dto/check-domain.dto.ts: - CheckDomainDto: domain (string, @IsString, @IsNotEmpty, @Matches regex for valid domain label — alphanumeric + hyphens, no dots) - Optional tlds (string array) — if omitted, use defaults
2. Create apps/api/src/domaincheck/domaincheck.service.ts:
   - DEFAULT_TLDS constant: ['de', 'com', 'net', 'org'] (per D-03 configurable, these are defaults)
   - Method checkDomain(domain: string, tlds?: string[]): Promise of array of { tld: string, fqdn: string, status: 'available' | 'registered' }
   - Implementation: use node:dns/promises — dns.resolve(fqdn) for each TLD variant
     - If resolves (any record) -> status = 'registered'
     - If throws with code ENOTFOUND or ENODATA -> status = 'available'
     - If throws with other error -> status = 'registered' (safe default)
   - Use Promise.allSettled for parallel lookups with 5-second timeout per lookup
   - Per D-02: system checks all TLD variants automatically from the base domain name

3. Create apps/api/src/domaincheck/domaincheck.controller.ts:
   - POST /modules/domaincheck/check
   - Protected by UseModule('domaincheck') decorator from module.guard.ts
   - Accepts CheckDomainDto body
   - Returns { domain: string, results: array of { tld, fqdn, status } }

4. Create apps/api/src/domaincheck/domaincheck.module.ts:
   - imports: PrismaModule, ModuleRegistryModule (for guard access)
   - providers: DomaincheckService
   - controllers: DomaincheckController

5. Create apps/api/src/domaincheck/domaincheck.seed.ts:
   - Export async function seedDomaincheckModule(moduleRegistryService: ModuleRegistryService)
   - Calls moduleRegistryService.seedModule with: slug='domaincheck', name='Domaincheck', version='1.0.0', category='domain-tools' (per D-09), description={ de: 'Domain-Verfuegbarkeit pruefen', en: 'Check domain availability' }, isSystem=true

6. Register DomaincheckModule in apps/api/src/app.module.ts imports.

7. Call the seed function from an OnModuleInit hook in DomaincheckModule (or add to an existing seeding mechanism) so the domaincheck module record exists in the database on startup.
cd /home/vicolab/projects/tessera-ctl && pnpm --filter api build 2>&1 | tail -3 - API build succeeds with DomaincheckModule - POST /modules/domaincheck/check accepts { domain: "google" } and returns results array with tld, fqdn, status for each default TLD - DNS check correctly identifies google.com as registered - Module seed creates a "domaincheck" record in Module table on startup - UseModule('domaincheck') guard protects the endpoint — returns 403 if module not activated for tenant Domaincheck API endpoint works end-to-end: accepts domain, checks DNS for TLD variants, returns structured results Task 2: Domaincheck Frontend — Input Form + Results Display apps/web/src/app/(portal)/modules/domaincheck/page.tsx, apps/web/src/app/(portal)/modules/domaincheck/components/DomainInput.tsx, apps/web/src/app/(portal)/modules/domaincheck/components/ResultList.tsx, apps/web/src/app/(portal)/modules/domaincheck/actions.ts, apps/web/messages/de.json, apps/web/messages/en.json apps/web/src/app/(portal)/page.tsx, apps/web/src/app/(portal)/layout.tsx, apps/web/messages/de.json, apps/web/messages/en.json 1. Add i18n keys to apps/web/messages/de.json under "domaincheck" namespace: - title: "Domaincheck" - description: "Pruefe ob eine Domain verfuegbar ist" - inputPlaceholder: "Domain eingeben (z.B. beispiel)" - checkButton: "Pruefen" - statusAvailable: "Verfuegbar" - statusRegistered: "Registriert" - noResults: "Gib einen Domain-Namen ein um die Verfuegbarkeit zu pruefen" - checking: "Pruefe..." - error: "Fehler bei der Pruefung"
2. Add same keys in English to apps/web/messages/en.json under "domaincheck" namespace:
   - title: "Domain Check"
   - description: "Check if a domain is available"
   - inputPlaceholder: "Enter domain (e.g. example)"
   - checkButton: "Check"
   - statusAvailable: "Available"
   - statusRegistered: "Registered"
   - noResults: "Enter a domain name to check availability"
   - checking: "Checking..."
   - error: "Error checking domain"

3. Create apps/web/src/app/(portal)/modules/domaincheck/actions.ts:
   - Server action or client-side fetch function checkDomainAction(domain: string): fetches POST to API_URL/modules/domaincheck/check with { domain }, passes auth cookie/token, returns typed results array

4. Create apps/web/src/app/(portal)/modules/domaincheck/components/DomainInput.tsx:
   - Client component ("use client")
   - Input field (text) with placeholder from i18n
   - Submit button
   - Calls onSubmit prop with the domain value
   - Shows loading state while checking (per i18n "checking" key)

5. Create apps/web/src/app/(portal)/modules/domaincheck/components/ResultList.tsx:
   - Client component
   - Receives results array prop: { tld, fqdn, status }[]
   - Renders list/table of results
   - Per D-01: green badge/pill for "available", red badge/pill for "registered"
   - Per D-02: shows all TLD variants with their status
   - Empty state shows "noResults" message

6. Create apps/web/src/app/(portal)/modules/domaincheck/page.tsx:
   - Page component within (portal) route group (gets AppShell layout)
   - Uses DomainInput and ResultList components
   - Manages state with useState: domain input, results array, loading, error
   - On submit: calls checkDomainAction, updates results
   - Layout: title at top, input below, results list below input (per specifics in CONTEXT.md)
   - Use Tailwind classes consistent with existing portal pages
   - Use shadcn/ui Card component for the module panel (per D-05a)
cd /home/vicolab/projects/tessera-ctl && pnpm --filter web build 2>&1 | tail -5 - Web app builds successfully with the new domaincheck route - /modules/domaincheck page renders within the portal AppShell - Input field accepts text, submit triggers API call - Results display with color-coded status badges (green=available, red=registered) - All visible strings use next-intl translations (no hardcoded text) - Works in both DE and EN locale User can navigate to /modules/domaincheck, enter a domain name, and see color-coded availability results for .de, .com, .net, .org TLD variants

<threat_model>

Trust Boundaries

Boundary Description
client -> POST /modules/domaincheck/check User-supplied domain string
API -> DNS External DNS resolution (network)

STRIDE Threat Register

Threat ID Category Component Disposition Mitigation Plan
T-03-05 Tampering domain input mitigate Regex validation in DTO — only alphanumeric + hyphens, max 63 chars, no dots allowed
T-03-06 Denial of Service DNS lookups mitigate 5-second timeout per lookup, max 10 TLDs per request, Promise.allSettled prevents hanging
T-03-07 Information Disclosure DNS results accept Domain availability is public information, no sensitive data exposed
T-03-08 Spoofing module access mitigate UseModule('domaincheck') guard + JwtAuthGuard ensure only authenticated users with activated module can access
</threat_model>
- pnpm --filter api build && pnpm --filter web build both succeed - POST /modules/domaincheck/check returns correct results for known domains - Frontend page renders and submits successfully - Module guard blocks access when module not activated for tenant

<success_criteria>

  • User can open /modules/domaincheck in browser (DCHK-01)
  • System checks TLD variants via DNS and returns status (DCHK-02)
  • Results show clear green/red status indicators (DCHK-03, D-01)
  • TLD list uses defaults ['de','com','net','org'] per D-03 </success_criteria>

Artifacts this phase produces

Symbol Location Type
DomaincheckService apps/api/src/domaincheck/domaincheck.service.ts class
DomaincheckController apps/api/src/domaincheck/domaincheck.controller.ts class
DomaincheckModule apps/api/src/domaincheck/domaincheck.module.ts NestJS module
CheckDomainDto apps/api/src/domaincheck/dto/check-domain.dto.ts class
seedDomaincheckModule apps/api/src/domaincheck/domaincheck.seed.ts function
DomaincheckPage apps/web/src/app/(portal)/modules/domaincheck/page.tsx React component
DomainInput apps/web/src/app/(portal)/modules/domaincheck/components/DomainInput.tsx React component
ResultList apps/web/src/app/(portal)/modules/domaincheck/components/ResultList.tsx React component
checkDomainAction apps/web/src/app/(portal)/modules/domaincheck/actions.ts function
Create `.planning/phases/03-module-system-domaincheck/03-02-SUMMARY.md` when done