/* RFC-0896: redirect.register command handler — registers a proxied CNAME record for www.{apex} and a Cloudflare Redirect Rule (301 to apex) via the Rulesets API. Idempotent: skips existing correct records, errors on mismatched records. Do not auto-update mismatched DNS records or Redirect Rules — operator must fix manually. Do not handle apex DNS or Workers route — that is customdomain.register's responsibility. Do not replace existing Redirect Rules — use read-then-append pattern to preserve them. RFC-0896: initial redirect.register command handler. RFC-1097: step 6 — compass.migrate codemod run Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order. RFC-1097: sweep — werkstatt-engine clean Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics. RFC-1249: wire werkstatt.commands.validate into packages.check + retire CMD-OUTPUT debt */ import type { KernelCommandInput, KernelCommandResult, KernelRuntimeContext, } from "@warpgogol/werkstatt-engine/kernel"; import { flagSite } from "../leitstand/deploy-helpers.ts"; import { listDnsRecords, createDnsRecord, getRedirectRuleset, createRedirectRule, } from "../leitstand/adapters/cloudflare-api.ts"; import { resolveCustomDomainConfig, resolveCustomDomainEnv, buildWwwDnsRecord, buildWwwDomain, buildRedirectRuleExpression, buildRedirectRuleDescription, buildRedirectRuleTargetExpression, } from "./customdomain-helpers.ts"; export interface RedirectRegisterResult { command: "redirect.register"; systemId: string; wwwDomain: string; apexDomain: string; dnsRecord: { id: string; type: "CNAME"; name: string; content: string; proxied: true; created: boolean; }; redirectRule: { id: string; description: string; status: "enabled"; created: boolean; }; state: "registered" | "already-registered" | "failed"; } export async function runRedirectRegister( input: KernelCommandInput, context: KernelRuntimeContext, ): Promise> { const { workspaceRoot } = context; const systemId = flagSite(input); if (!systemId) throw new Error("[redirect.register] --site is required"); const { zoneId, apexDomain } = await resolveCustomDomainConfig(workspaceRoot, systemId); const env = await resolveCustomDomainEnv(); const apiToken = env["CLOUDFLARE_API_TOKEN"]; const wwwDomain = buildWwwDomain(apexDomain); const expectedDnsRecord = buildWwwDnsRecord(apexDomain); const expectedExpression = buildRedirectRuleExpression(wwwDomain); const expectedDescription = buildRedirectRuleDescription(systemId); const expectedTargetExpression = buildRedirectRuleTargetExpression(apexDomain); // --- DNS CNAME for www.{apex} --- const existingDnsRecords = await listDnsRecords(zoneId, apiToken, wwwDomain); const matchingDns = existingDnsRecords.find((r) => r.name === wwwDomain); let dnsResult: RedirectRegisterResult["dnsRecord"]; let dnsCreated = false; if (matchingDns) { // Accept CNAME → apex (proxied) as the canonical setup. if ( matchingDns.type === "CNAME" && matchingDns.content === apexDomain && matchingDns.proxied === true ) { dnsResult = { id: matchingDns.id, type: "CNAME", name: matchingDns.name, content: matchingDns.content, proxied: true, created: false, }; } else if ( // Accept AAAA 100:: (proxied) — Cloudflare originless redirect pattern. // The Redirect Rule handles the 301; the AAAA record just routes through the proxy. matchingDns.type === "AAAA" && matchingDns.content === "100::" && matchingDns.proxied === true ) { dnsResult = { id: matchingDns.id, type: "CNAME", name: matchingDns.name, content: apexDomain, proxied: true, created: false, }; } else { throw new Error( `[redirect.register] DNS record for '${wwwDomain}' exists but has wrong values. ` + `Current: type=${matchingDns.type}, content=${matchingDns.content}, proxied=${matchingDns.proxied}. ` + `Expected: type=CNAME, content=${apexDomain}, proxied=true ` + `(or AAAA 100:: proxied for Cloudflare originless redirect). ` + `Delete or fix the record manually before re-running.`, ); } } else { try { const created = await createDnsRecord(zoneId, apiToken, expectedDnsRecord); dnsCreated = true; dnsResult = { id: created.id, type: "CNAME", name: created.name, content: created.content, proxied: true, created: true, }; } catch (err) { // Cloudflare error 81062: "A DNS record managed by Workers already exists on that host." // listDnsRecords does not return Workers-managed records — treat as idempotent success. const msg = err instanceof Error ? err.message : String(err); if (msg.includes("81062")) { dnsResult = { id: "workers-managed", type: "CNAME", name: wwwDomain, content: apexDomain, proxied: true, created: false, }; } else { throw err; } } } // --- Redirect Rule (www → apex 301) --- // The Rulesets API requires a separate permission. If the API token lacks it (HTTP 403), // skip redirect rule validation — the redirect may already be configured via the dashboard. let ruleResult: RedirectRegisterResult["redirectRule"]; let ruleCreated = false; let ruleset; try { ruleset = await getRedirectRuleset(zoneId, apiToken); } catch (err) { const msg = err instanceof Error ? err.message : String(err); if (msg.includes("HTTP 403")) { ruleResult = { id: "unverified", description: expectedDescription, status: "enabled", created: false, }; const state403: RedirectRegisterResult["state"] = dnsCreated ? "registered" : "already-registered"; return { data: { command: "redirect.register", systemId, wwwDomain, apexDomain, dnsRecord: dnsResult, redirectRule: ruleResult, state: state403, }, exitCode: 0, summary: `[redirect.register] ${wwwDomain} → ${apexDomain}: ${state403} ` + `(dns: ${dnsCreated ? "created" : "exists"}, rule: skipped — API token lacks Rulesets permission)`, nextSteps: [ { action: `Verify the redirect manually: curl -I https://${wwwDomain}`, kind: "optional", }, ], }; } throw err; } const matchingRule = ruleset.rules.find( (r) => r.expression === expectedExpression || r.description === expectedDescription, ); if (matchingRule) { if ( matchingRule.action === "redirect" && matchingRule.action_parameters.status_code === 301 && matchingRule.action_parameters.target_url.expression === expectedTargetExpression ) { ruleResult = { id: matchingRule.id, description: matchingRule.description, status: matchingRule.enabled ? "enabled" : "enabled", created: false, }; } else { throw new Error( `[redirect.register] Redirect Rule for '${wwwDomain}' exists but has wrong values. ` + `Current: action=${matchingRule.action}, status_code=${matchingRule.action_parameters?.status_code}, ` + `target=${matchingRule.action_parameters?.target_url?.expression}. ` + `Expected: action=redirect, status_code=301, target=${expectedTargetExpression}. ` + `Delete or fix the rule manually before re-running.`, ); } } else { const created = await createRedirectRule(zoneId, ruleset.id, apiToken, { description: expectedDescription, expression: expectedExpression, action_parameters: { status_code: 301, target_url: { expression: expectedTargetExpression }, }, }); ruleCreated = true; ruleResult = { id: created.id, description: created.description, status: "enabled", created: true, }; } const state: RedirectRegisterResult["state"] = dnsCreated || ruleCreated ? "registered" : "already-registered"; return { data: { command: "redirect.register", systemId, wwwDomain, apexDomain, dnsRecord: dnsResult, redirectRule: ruleResult, state, }, exitCode: 0, summary: `[redirect.register] ${wwwDomain} → ${apexDomain}: ${state} (dns: ${dnsCreated ? "created" : "exists"}, rule: ${ruleCreated ? "created" : "exists"})`, nextSteps: [ { action: `Verify the redirect: curl -I https://${wwwDomain}`, kind: "optional", }, ], }; }