Hostname swap step 1 — add additive api.pal-e-docs funnel via pal-e-deployments kustomize #278
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/pal-e-platform#278
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type
Feature
Lineage
Discovered during 2026-04-11 architecture-routing review with Lucas. The product name (
pal-e-docs.tail5b443a.ts.net) currently points at the FastAPI backend; the SvelteKit frontend lives atpal-e-production.tail5b443a.ts.net. This is the first step of a 7-step migration to swap the routing.Scope review round 1: NEEDS_REFINEMENT (review-972-2026-04-11). Round 1 reviewer correctly identified that the original ticket scope (5 repos, 9 ACs, multi-step coordinated migration) violated the 5-minute rule. Round 2 addresses this by shrinking #278 to scope ONLY Step 1 (additive funnel — purely additive, zero blast radius) and filing the remaining 6 steps as a separate follow-up roadmap ticket.
Round 1 also raised these findings, addressed below:
arch-domain-pal-e-docsdoes not exist — REFUTED. Verified viamcp__pal-e-docs__list_notes(note_type="architecture", project="pal-e-docs"): the note exists with id=1410, created 2026-04-10. The round 1 reviewer's search missed it.pal-e-production-funnel. The unmanagedpal-e-docs-funnelis NOT touched in this ticket — only an additive new funnel is created.arch-deployment-pal-e-docs(the third leg of the triplet) and is out of scope here.Repo
forgejo_admin/pal-e-deployments— kustomize overlays for ArgoCD. Thepal-e-production-funnelprecedent lives atoverlays/pal-e-production/prod/ingress.yaml; this ticket adds a parallel overlay for the new API funnel.User Story
As the Superuser (Lucas)
I want a new
api.pal-e-docs.tail5b443a.ts.netTailscale funnel that serves the existing FastAPI backendSo that the API has a stable forward-facing hostname that announces it is an API, with zero impact on the existing
pal-e-docs.tail5b443a.ts.netingress (no risk to current consumers)Traces to:
story:reader-browseonproject-pal-e-docs.Architecture
arch:notes— thenotescomponent inarch-domain-pal-e-docs(id 1410). This funnel exposes the API surface that serves note-shaped resources.arch:k8s-deploy— known traceability debt; no backing arch note exists yet (see Lineage)Context
Current state (verified live 2026-04-11):
pal-e-docs-funnelpal-e-docspal-e-docs(FastAPI)pal-e-production-funnelpal-e-production~/pal-e-deployments/overlays/pal-e-production/prod/ingress.yamlThis ticket adds a third funnel:
pal-e-docs-api-funnel(new)pal-e-docspal-e-docs(FastAPI, same namespace as the funnel — same-namespace ingress constraint satisfied trivially)After this ticket lands, both
pal-e-docs.tail5b443a.ts.netANDapi.pal-e-docs.tail5b443a.ts.netwill serve the API. They are functionally redundant — the new hostname is additive only. No swap, no removal, no risk to existing consumers. The swap happens in a later ticket (Step 6).Why Option B (kustomize) and not Option A (terraform import):
pal-e-production-funnelalready uses kustomize. Matching the precedent is lower friction than introducing a parallel terraform-import code path.pal-e-docs-funnelis left untouched — no riskyterraform importstep.pal-e-platform/CLAUDE.md: "Cluster: k3s with Tailscale funnels for ingress and TLS" — funnels CAN live in either layer; matching the existing per-app pattern is the lower-risk call.Rationale (from round 1 — preserved verbatim because it remains the canonical decision record)
Considered three options for the overall migration: subdomain split, path-based split, content negotiation. Chose subdomain because:
/notes/{slug}to/api/notes/{slug}, rippling throughpal-e-sdk,pal-e-mcp, every CLAUDE.md curl example, every agent prompt, and every in-flight ticket. Subdomain requires changing two constants (SDK base URL and MCP base URL) plus a kustomize overlay.convention-spa-no-subpath-proxyis a hard constraint. Vite assets are absolute from/. Even if the SPA stays at root and only the API moves to/api/*, this paints us into a corner the moment a second SPA wants a subpath.adapter-nodeSSR hits the API backend-to-backend; cookies don't traverse host boundaries. With CORS middleware (#256), browser-side fetches work from a separate subdomain just fine.pal-e-docs,pal-e-production,pal-e-streamlit). The fix is renaming/swapping within an existing pattern, not introducing a new architecture.Rejected content negotiation because it hides routing in code (varies-by-Accept caching pain), and would either reintroduce in-process Jinja2 (which Phase F35 explicitly sunset) or add a SvelteKit upstream proxy (complexity without benefit).
File Targets
Single repo, single PR:
overlays/pal-e-docs-api/prod/kustomization.yaml(new) — kustomization referencingingress.yamloverlays/pal-e-docs-api/prod/ingress.yaml(new) — Kubernetes Ingress + Tailscale funnel annotations forapi.pal-e-docs.tail5b443a.ts.net, backend servicepal-e-docsinpal-e-docsnamespace, port matching the existingpal-e-docs-funnelservice portUse
~/pal-e-deployments/overlays/pal-e-production/prod/ingress.yamlas the template — copy structure, change hostname, change backend service ref, change name labels.Files NOT to touch:
pal-e-docs-funnelingress (the existing one) — explicitly not touched in this stepoverlays/pal-e-production/— frontend stays where it isAcceptance Criteria
overlays/pal-e-docs-api/prod/exists withkustomization.yamlandingress.yamlingress.yamldefines a Kubernetes Ingress namedpal-e-docs-api-funnel(or similar) in thepal-e-docsnamespace, with hostapi.pal-e-docs.tail5b443a.ts.netand Tailscale funnel annotations matching thepal-e-production-funnelprecedentpal-e-docsservice in thepal-e-docsnamespace, port matching the existing funnelkubectl -n pal-e-docs get ingressshows BOTHpal-e-docs-funnel(existing, untouched) AND the newpal-e-docs-api-funnelcurl -I https://api.pal-e-docs.tail5b443a.ts.net/notes/arch-westside-emailsreturns200withcontent-type: application/jsonpal-e-docs.tail5b443a.ts.netis unchanged — all current consumers continue working as beforeTest Expectations
kubectl get ingress -n pal-e-docsbaseline capturedcurl -I https://pal-e-docs.tail5b443a.ts.net/notes/arch-westside-emailsreturns 200 (regression check on existing hostname)curl -I https://api.pal-e-docs.tail5b443a.ts.net/notes/arch-westside-emailsreturns 200 (new hostname works)curl -s https://api.pal-e-docs.tail5b443a.ts.net/notes/arch-westside-emails | jq .idreturns the same id as the existing hostname (same backing service, content parity)~/.mcp.json(or equivalent) to point pal-e-docs MCP at the new hostname, runlist_notes(limit=1), verify a result is returned. Then revert the MCP config — this is a smoke test only, NOT a permanent migration (that's Step 3).Constraints
pal-e-docs-funnelingressoverlays/pal-e-production/prod/exactly — same labels, same annotation style, same file structurekubectl delete ingress pal-e-docs-api-funnel -n pal-e-docsif anything goes wrongChecklist
pal-e-deploymentsmainoverlays/pal-e-production/prod/Related
forgejo_admin/pal-e-api#256— dependency: CORS middleware. Even though Step 1 doesn't change browser fetches, the new API hostname must still serve correct CORS headers when consumed by the frontend in later steps.forgejo_admin/pal-e-platform#280— follow-up: hostname swap roadmap (Steps 2-7) — to be filed once this ticket is in flight or just beforearch-domain-pal-e-docs(id 1410) — the architecture diagramconvention-spa-no-subpath-proxy— the constraint that ruled out path-based routing~/pal-e-deployments/overlays/pal-e-production/prod/ingress.yaml— the template to copy fromreview-972-2026-04-11— the round 1 review that drove this decompositionScope Review: NEEDS_REFINEMENT
Review note:
review-972-2026-04-11Rationale is strong (subdomain-vs-path-vs-content-neg decision is the best part of this ticket and should survive decomposition verbatim). Blockers are scoping gaps in file targets, traceability, and size.
Blockers:
terraform/k3s-funnels.tfdoes not exist. Live cluster inspection showspal-e-docs-funnelis unmanaged (hand-applied 2026-02-24, no argocd label, no terraform, no kustomize) andpal-e-production-funnelis managed by pal-e-deployments (ArgoCD), NOT pal-e-platform. Must decide Option A (import into pal-e-platform terraform networking module — triggers cross-namespace ingress refactor) vs Option B (manage via pal-e-deployments kustomize overlays — this ticket then belongs in pal-e-deployments, not pal-e-platform). Repo placement is likely wrong.arch:k8s-deployhas no backing note.arch-k8s-deploydoesn't exist in pal-e-docs, and the tag itself isn't registered (onlyarch:jersey-intakeis a real arch tag). Same gap blocks sibling #279. Create the arch note with the project-pal-e-docs#architecture mermaid as seed content.arch:noteshas no backing note, andarch-domain-pal-e-docs(cited in body and Related) does not exist as a slug. Hallucinated reference — either create the note or point Related atproject-pal-e-docs#architecture.skill-decompose-ticketwith seeds 972a–972g (additive-funnel → sdk → mcp → api-cors-env → app → swap → decom) from the review note. Each sub-ticket is a clean single-repo, single-PR, <5-min pass. Keep #278 as umbrella; preserve the Rationale section.Non-blockers (also fix during refine):
Verified good:
Not ready for todo. Return to Ava for SCOPE decisions (arch notes + Option A/B), refine, re-review.
Swap hostname routing — pal-e-docs.ts.net serves frontend, api.pal-e-docs.ts.net serves APIto Hostname swap step 1 — add additive api.pal-e-docs funnel via pal-e-deployments kustomizeScope Review: APPROVED (round 2)
Review note:
review-972-2026-04-11-r2Round 2 resolves every round 1 finding. Scope is now single-repo, two-new-files, additive-only, fits the 5-minute rule.
Round 1 findings — status:
arch-domain-pal-e-docsmissing — refuted, note id=1410 exists (round 1 search missed it)overlays/pal-e-production/prod/ingress.yamlverifiedarch:k8s-deployno backing note — accepted as known debt, consistent with sibling tickets #234/#613/#973pal-e-docsnamespaceVerification performed:
arch-domain-pal-e-docsid=1410 confirmed livestory:reader-browseconfirmed inproject-pal-e-docs#user-storiestable~/pal-e-deployments/overlays/pal-e-docs/prod/confirmed to NOT contain aningress.yaml(existing funnel is unmanaged — safe to add a new overlay next to it)pal-e-docsconfirmed in existing kustomizationDecomposition: No further split. 2 files, 1 repo, 8 ACs that are all verification facets of one atomic change.
Minor non-blocking nit:
[LABEL]Board item #972 display title still reflects round 1 framing ("Swap hostname routing…"). Optional: update to match current issue title. Not a blocker.Ready to advance backlog → todo when Ava dispatches.