OBJ-EDGE-DWETOPI-001: Subdominios de desarrollo *.d.wetopi.com en el edge v1¶
Objective ID: OBJ-EDGE-DWETOPI-001 Status: completed (2026-09-10 — TODAS las fases ejecutadas y verificadas: cert staging/prod Ready, DNS live, Gateway certRef aplicado, SNI público verificado, co-tenants sin regresión; ver SVC-DEV-WILDCARD) Drafted by: wetopi-lead (Orchestrator) — Owned by: platform Ejecución: wetopi-coder (PRs) · Applies: operator (DNS tofu + Gateway)
Outcome¶
https://<wepod>.d.wetopi.com responde desde internet con un certificado
Let's Encrypt production wildcard válido (*.d.wetopi.com, DNS-01 vía
Cloudflare) terminado en el Gateway kube-system/wetopi-edge (Traefik,
edge v1 según ADR-0006), con routing catch-all a los backends que los
wepods registren. Añadir un wepod nuevo NO toca el edge: el equipo de
desarrollo crea (o reutiliza) una HTTPRoute con hostname
<wepod>.d.wetopi.com — o un backend en la route catch-all — y el dominio
funciona sin tickets de DNS, sin certificados nuevos y sin applies.
Why¶
des el sandbox de iteración (decisión rev1 2026-09-09: wepods de prueba sin mecánica canary): el flujo de despliegue dev debe ser rápido; un cert por dominio rompe esa velocidad (LE prod rate limits + espera de emisión + certRef por servicio en el Gateway con su apply manual).- Requisito del operador (2026-09-10): exponer muchos subdominios
*.d.wetopi.com(subdominios de desarrollo de wetopi) con 443, optimizando el tiempo de despliegue → wildcard. - La maquinaria ya existe y está live-verified: edge v1 (ADR-0006),
cert-manager + LE prod DNS-01 (4+ certificados prod emitidos por esa
cadena), Gateway
wetopi-edgecon 11 certRefs y DNS tofu-owned. El wildcard es una adición declarativa al patrón probado, no un sistema nuevo. - ADR-0019 (proposed, §D-tls DECIDIDO) fija el modelo TLS: offloading en
Traefik, HTTP interno hasta el wepod — exactamente lo que este objetivo
monta para
d.wetopi.com.
Decisions (bound for executor)¶
- D1 — Wildcard LE PROD, no cert por servicio. Un único Certificate
dev-wildcard-tls(kube-system,dnsNames: ["*.d.wetopi.com"], issuerletsencrypt-prod). Justificación: velocidad de despliegue (requisito del operador), 1 challenge DNS-01 (vs 1 por subdominio = rate-limit LE), rotación única. El precedente "no wildcard" de certificate-infer.yaml protegía*.wetopi.netcompartido entre servicios de platforma; aquí el scope del wildcard es un dominio de desarrollo (.d.), de blast radius delimitado — y los hosts siguen estando en manos de GitOps, no del Secret. - D2 — Token Cloudflare RESUELTO (operador 2026-09-10): token existente
ampliado a la zona wetopi.com (misma referencia BY NAME
cloudflare-dns-api-tokenen ns cert-manager; sin Secret nuevo ni issuers clonados). Verificación en la emisión staging (solver con zone-discovery sobre wetopi.com); si el token hubiese rotado de valor, rotar el Secret desde el env 0600 de ops01 antes de emitir. - D3 — CNAME apex de la zona de servicio. En vez de 2 A records por
subdominio, se declara UN registro:
d.wetopi.com CNAME/A→ los 2 edge IPs, y cadawepod.d.wetopi.comes implícito (resuelve por el CNAME del apex si el proveedor lo permite) — PERO Cloudflare NO permite CNAME en apex hacia fuera de la zona: la implementación tofu son los 2 A recordsd→ 79.137.114.193/.194 (grey-cloud, TTL 300, mismo patrón que edge-test/docs/vault). Los subdominioswepod.*se sirven por el mecanismo del resolver + el HTTPRoute catch-all: cualquier subdominio NO declarado explícitamente en Cloudflare NO resuelve → D3bis. - D3bis — ¿wildcard DNS o subdominios explícitos? Cloudflare free/pro
sí permite registros wildcard (
*.d.wetopi.comA .193/.194) grey-cloud. Decisión por defecto: declarar el wildcard DNS (1 entrada tofu, 2 records) + el cert wildcard — cualquierwepod.d.wetopi.comresuelve y sirve al momento sin tocar DNS nunca más. Riesgo aceptado y documentado: cualquier subdominio resuelve al edge (igual que hoy hace*.wetopi.net→ .76 legacy). El operador puede pedir la variante conservadora (subdominios explícitos por wepod en tfvars). - D4 — Routing: per-wepod route exacta (primario) + catch-all opcional.
Se crea namespace
wepods-d. Mecanismo primario de onboarding (probado en producción): cada wepod declara SU HTTPRoute con hostname EXACTO (wepod.d.wetopi.com) + Service en su namespace (o enwepods-d), parentRef al Gateway wetopi-edge sectionName https (allowedRoutes from:All ya abierto) — el mismo patrón que docs-site/argo sirven hoy. El cert wildcard los cubre a todos sin tocar cert-manager. Mejora opcional a validar en build: HTTPRoute catch-alldev-catchallconhostnames: ["*.d.wetopi.com"]+ backend default 404 (el soporte de hostname wildcard en HTTPRoute por el provider Gateway de Traefik no está documentado explícitamente — validarlo con un wepod real antes de depender de él). - D5 — Traefik no requiere config extra: el provider Gateway de Traefik enlaza las HTTPRoutes nuevas al vuelo (recarga automática observada en los edge-change flows); el matching de hosts es por las hostnames de cada route (exactas en el patrón probado). No se toca HelmChartConfig (8000/8443 + REDIRECT intactos).
- D6 — No HTTP-01: el edge solo expone 80/443 con el patrón web→websecure ya probado; DNS-01 es port-mapping-immune (SVC-CERT-MANAGER).
- D7 — Wepods NO van en kube-system: namespace dedicado
wepods-d(o uno por wepod si el aislamiento lo pide — decisión en build). Los Certificates/Secrets del Gateway viven en kube-system; las HTTPRoutes de wepods enwepods-d(allowedRoutes from:All ya configurado en el listener https).
Acceptance criteria (DONE)¶
| # | AC | Verificación |
|---|---|---|
| AC1 | Cert wildcard LE PROD dev-wildcard-tls Ready en kube-system |
kubectl -n kube-system get certificate dev-wildcard-tls -o jsonpath='{.status.conditions[*].type}' = Ready; openssl s_client -connect <edge>:443 -servername demo-joan.d.wetopi.com muestra *.d.wetopi.com en SAN, issuer LE |
| AC2 | Gateway https listener ResolvedRefs=True + Programmed=True con el nuevo certRef | kubectl -n kube-system get gateway wetopi-edge -o jsonpath='{.status.conditions}' — sin regresión de co-tenant (docs/argo/vault/infer/grafana/prometheus/alertmanager/cerebro/otlp sirven 200/401/403 esperados) |
| AC3 | DNS: demo-joan.d.wetopi.com y d.wetopi.com resuelven a .193/.194 |
dig +short demo-joan.d.wetopi.com ×2 IPs; declarado en cloudflare_dns_records (tfvars) |
| AC4 | HTTPS público demo: https://demo-joan.d.wetopi.com responde con la route catch-all (404 default o backend del wepod demo) |
curl -sSI desde ops01 → respuesta con cert válido; SAN *.d.wetopi.com; sin 5xx de infraestructura |
| AC5 | Un wepod nuevo se expone SIN tocar el edge: añadir Service + entry en la HTTPRoute (o nueva HTTPRoute) es el único cambio | simulado en PR: 2º wepod añadido por manifest; verificación de que NO hay delta en cert-manager ni en el Gateway |
| AC6 | Sin regresión de seguridad: 401/403 en docs/argo/vault/infer/memoria/grafana/prometheus/alertmanager/cerebro/otlp siguen como hoy | re-run de los probes públicos de co-tenant (checklist go-live del skill wetopi-gitops-ops) |
Verification contract¶
- Todo AC con salida de comando real en el ledger (sin verify=False).
- DNS apply y Gateway apply: cards ops + hard gate operador (mismo patrón que el cutover docs .76 → .193/.194 y F5).
- Certificado: fail-safe order obligatorio (Certificate Ready ANTES del
certRef en el Gateway — un Secret ausente tumba el listener completo,
SVC-TRAEFIK-EDGE). Staging-first: emitir el wildcard primero contra
letsencrypt-stagingpara validar el solver en la zona wetopi.com, y CORTAR a prod solo tras el gate (protege el rate budget de LE).
Constraints¶
- ADR-0006 ventana v1 sigue vigente: wepods DEV son operator-only por ahora (no external paying customers — la condición dura del ADR se mantiene: v2 edge antes de clientes externos). El carácter "dev" se defiende con: (a) el namespace no es productivo, (b) el default 404, (c) futura capa WAF/IP-allowlist según ADR-0019 (out of scope aquí).
- Ningún delta sobre edge_lb nftables ni HelmChartConfig (contrato "three concerns together" intacto).
- El cert wildcard NO se reutiliza para otros dominios (no
*.wetopi.com). - Secret del token Cloudflare wetopi.com: operator material, vault + Secret k8s por nombre; NUNCA en Git.
- Build ≠ Apply: PRs declarativos; applies de DNS/tofu y Gateway = cards ops con gate humano (runbook del repo edge).
Boundaries¶
- No se toca la zona wetopi.net ni sus records (solo se AÑADE material en wetopi.com + kube-system).
- No se despliegan wepods productivos ni clientes externos (ADR-0006 hard condition 1 sigue).
- No L4 (v2 edge), no WAF, no egress policies (ADR-0019 track, out of scope).
Stop conditions¶
- ~~El token Cloudflare para wetopi.com no se puede provisionar → BLOCK~~ RESUELTO 2026-09-10 (operador amplió el token a ambas zonas). Nueva condición: si la emisión STAGING falla por permisos del token ampliado (zone-discovery sobre wetopi.com) tras 2 intentos limpios → BLOCK con logs del solver y escalar al operador.
- LE staging falla en la zona wetopi.com tras 2 intentos limpios → BLOCK con logs del solver (zona discovery / permisos del token).
- Cualquier regresión de co-tenant tras el Gateway apply → ROLLBACK inmediato (revert del certRef) + incident record.
- Si el operador revoca la excepción para wepods dev → abandon (revert DNS wildcard + certRef).
Review boundaries¶
- PRs: infrastructure (tfvars DNS + certificate-dev-wildcard.yaml), platform-edge (Gateway certRef), platform-gitops (nueva Application wepods-d si el routing catch-all va GitOps).
- Review independiente obligatoria (approver) para el cert wildcard y el Gateway delta (patrón F2/F3: el operador ejecuta applies).
- Lead no aprueba su propio ADR; reviewer = wetopi-reviewer/approver.
Out of scope¶
- Capa L4 / v2 edge (VRRP/IPVS/DSR) — precondición futura para clientes externos, no para wepods dev.
- WAF per-site (ADR-0019 D-waf-toggle) — siguiente iteración de seguridad para estos mismos hostnames.
- Egress policies de wepods (ADR-0019 perfiles egress) — plano aparte.
- TLS re-encrypt por-pod (rechazado en ADR-0019 D-tls).
Promotion matrix¶
- Éxito → ficha SVC
platform/edge/subdomains-dev.md(o sección en SVC-TRAEFIK-EDGE) + update del id-registry + Hindsight. - Decisión wildcard → ADR-0020 (attached).
- Si el operador elige subdominios explícitos → mismo OBJ con D3bis variante B (solo cambia el tfvars).