Files
proxmox/docs/02-architecture/SANKOFA_PHOENIX_CONSOLIDATED_FRONTEND_AND_API.md
2026-04-13 21:41:14 -07:00

12 KiB
Raw Blame History

Sankofa Phoenix — consolidated non-chain frontend and API hub

Status: Architecture proposal (resource conservation)
Last updated: 2026-04-13
LAN status (operator): Tier-1 API hub nginx on VMID 7800 listening http://192.168.11.50:8080 (sankofa-phoenix-api-hub.service). Apollo (Fastify) binds 127.0.0.1:4000 only (HOST=127.0.0.1 in /opt/sankofa-api/.env; apply: scripts/deployment/ensure-sankofa-phoenix-apollo-bind-loopback-7800.sh). NPM → :8080 + WebSocket upgrades is live for phoenix.sankofa.nexus (fleet 2026-04-13). Install hub: scripts/deployment/install-sankofa-api-hub-nginx-on-pve.sh with PROXMOX_OPS_APPLY=1 + PROXMOX_OPS_ALLOWED_VMIDS=7800. Readiness: scripts/verify/verify-sankofa-consolidated-hub-lan.sh, hub GraphQL scripts/verify/smoke-phoenix-api-hub-lan.sh, WebSocket upgrade scripts/verify/smoke-phoenix-graphql-wss-public.sh (pnpm run verify:phoenix-graphql-wss), graphql-ws handshake pnpm run verify:phoenix-graphql-ws-subscription, hub /graphql-ws headers scripts/deployment/ensure-sankofa-phoenix-api-hub-graphql-ws-proxy-headers-7800.sh.

r630-01 load goal: consolidating frontends and moving hub LXCs to quieter nodes is what reduces guest count and hypervisor pressure — see SANKOFA_R630_01_CONSOLIDATION_AND_HUB_PLACEMENT_GOAL.md.

Ecosystem shape (non-chain, hyperscaler-style): NON_CHAIN_ECOSYSTEM_HYPERSCALER_STYLE_MODEL.md (cell types, edge vs chain plane).

Scope: Non-blockchain Sankofa / Phoenix surfaces only. Out of scope: Chain 138 explorer, Besu/RPC, CCIP/relayers, token-aggregation compute — keep those on dedicated LXCs/VMs per existing runbooks.


1. Problem

Today, multiple LXCs/VMIDs often run one primary workload each (portal, corporate web, Phoenix API, DBIS API, gov dev shells, etc.). Each Node or Next process carries base RAM (V8 heap, file watchers in dev, separate copies of dependencies). Nginx-only static sites are cheap; many separate Node servers are not.

This document defines a consolidated runtime that:

  1. Puts all non-chain web frontends behind one LAN endpoint (one LXC or one Docker host — your choice), using static-first or one Node process where SSR is required.
  2. Puts all Phoenix-facing backend traffic behind one logical API (one public origin and port): GraphQL (current Phoenix), REST/BFF (dbis_core and future middleware), health, and webhooks.

Canonical surface taxonomy remains SANKOFA_PHOENIX_CANONICAL_BOUNDARIES_AND_TAXONOMY.md. Consolidation changes packaging, not the names of visitor vs client vs operator paths.


2. Single “web hub” LXC (frontends)

2.1 Option A — Static-first (lowest RAM)

When: Marketing pages, IRU/marketplace after static export, simple entity microsites, post-login SPAs that call the API hub only.

  • Build: next build with output: 'export' where compatible (no server-only APIs on those routes).
  • Serve: nginx with one server per FQDN (server_name) or one server + map $host $site_root → different root directories under /var/www/....
  • NPM: All affected FQDNs point to the same upstream http://<WEB_HUB_IP>:80.

Tradeoff: NextAuth / OIDC callback flows and server components need either client-only OIDC (PKCE) against Keycloak or a small SSR slice (see option B).

2.2 Option B — One Node process for all SSR Next apps (moderate RAM)

When: Portal (portal.sankofa.nexus), admin, or any app that must keep getServerSideProps, NextAuth, or middleware.

  • Monorepo (e.g. Turborepo/Nx): multiple Next “apps” merged into one deployable using:
    • Next multi-zone (primary + mounted sub-apps), or
    • Single Next 15 app with middleware.ts rewriting by Host, or
    • Single custom server (less ideal) proxying to child apps — avoid unless necessary.

Outcome: One node process (or one standalone output + one PID supervisor) on one port (e.g. 3000). Nginx in front optional (TLS termination usually at NPM).

2.3 Option C — Hybrid (practical migration)

  • nginx: static corporate apex, static entity sites, docs mirrors.
  • One Node: portal + Phoenix “shell” that must stay dynamic.

Still fewer LXCs than “one LXC per microsite.”

2.4 What stays out of this box

  • Blockscout / explorer stacks
  • info.defi-oracle.io, MEV GUI, relay health — separate nginx LXCs as today unless you explicitly merge static mirrors only
  • Keycloak — keep separate (identity is its own security domain)

3. Single consolidated API (Phoenix hub)

3.1 Responsibilities

Path family Today (typical) Hub role
/graphql, /graphql-ws Phoenix VMID 7800 :4000 Reverse proxy to existing Apollo until merged in code
/api/v1/*, /api-docs dbis_core (e.g. :3000) Reverse proxy mount
/health Multiple Aggregate (optional): hub returns 200 only if subgraphs pass
Future BFF N/A Implement in hub (session, composition, rate limits)

Naming: Introduce an internal service name e.g. sankofa-phoenix-hub-api. Public FQDN can remain phoenix.sankofa.nexus or split to api.phoenix.sankofa.nexus for clarity; NPM decides.

3.2 Implementation tiers (phased)

Tier 1 — Thin hub (fastest, lowest risk)
One process: nginx or Caddy. Typical production pattern: hub on its own LXC or same CT as Apollo — proxy_pass Phoenix to 127.0.0.1:4000 when colocated, and dbis_core to IP_DBIS_API:3000 (LAN) as in install-sankofa-api-hub-nginx-on-pve.sh. Single public port (e.g. 443 behind NPM → 8080 on the hub). Before NPM sends public traffic to the hub, validate TRUST_PROXY and trusted proxy hops for dbis_core (see NON_CHAIN_ECOSYSTEM_PLAN_REVIEW_AND_GAPS.md §2.1).

Tier 2 — Application hub
Single Node (Fastify/Express) app: validates JWT once, applies rate limits, proxy to subgraphs, adds BFF routes (/bff/portal/...).

Tier 3 — Monolith (long-term)
Merge routers and schema into one codebase — only after boundaries and ownership are clear.

3.3 Middleware cross-cutting

Centralize in the hub:

  • CORS allowlist (origins = web hub FQDNs only)
  • Rate limiting (especially IRU public POST — align with dbis_core TRUST_PROXY=1 and a trusted proxy list that includes NPM and this hubs LAN IP, or rate limits see only the hub)
  • Request ID propagation
  • mTLS or IP allowlist for operator-only routes (optional)

4. NPM and inventory

After cutover:

  • Fewer distinct upstream IPs in NPM (many FQDNs can point at the same IP:port); NPM may still use one proxy host record per FQDN for TLS—equivalent to one ALB with many listener rules, not literally one row total. Host-based routing then lives in web hub nginx (server_name / map) or in Next middleware.ts.
  • Update ALL_VMIDS_ENDPOINTS.md and get_host_for_vmid in scripts/lib/load-project-env.sh when VMIDs are retired or replaced by hub VMIDs.
  • config/ip-addresses.conf defines optional hub variables that default to the current discrete CT IPs (IP_SANKOFA_WEB_HUB → portal IP, IP_SANKOFA_PHOENIX_API_HUB → Phoenix API IP). Override in .env when hub LXCs exist.

5. Concrete file references in this repo

Artifact Purpose
config/nginx/sankofa-non-chain-frontends.example.conf Example host → static root nginx for web hub
config/nginx/sankofa-phoenix-api-hub.example.conf Example path → upstream for API hub (Tier 1); tune upstream to LAN or 127.0.0.1 when colocated
config/nginx/sankofa-hub-main.example.conf Top-level nginx.conf for web hub CT (-c for systemd)
config/nginx/sankofa-api-hub-main.example.conf Top-level nginx.conf for API hub CT
config/systemd/sankofa-non-chain-web-hub-nginx.service.example systemd unit for web hub nginx
config/systemd/sankofa-phoenix-api-hub-nginx.service.example systemd unit for API hub nginx
config/compose/sankofa-consolidated-runtime.example.yml Optional Docker Compose sketch (API hub container only)
scripts/verify/check-sankofa-consolidated-nginx-examples.sh nginx -t on example snippets (host nginx or Docker fallback)
scripts/deployment/plan-sankofa-consolidated-hub-cutover.sh Read-only cutover reminder + resolved env from load-project-env.sh
scripts/deployment/install-sankofa-api-hub-nginx-on-pve.sh Tier-1 hub install on CT (--dry-run / --apply + PROXMOX_OPS_*)
scripts/verify/verify-sankofa-consolidated-hub-lan.sh Read-only LAN smoke (Phoenix, portal, dbis /health, Keycloak realm)

6. Operator cutover checklist (complete in order)

  1. Run bash scripts/verify/check-sankofa-consolidated-nginx-examples.sh (CI or laptop).
  2. Provision one non-chain web hub LXC and/or one API hub LXC (or colocate nginx on an existing CT — document the choice).
  3. Copy and edit nginx snippets from config/nginx/ into /etc/sankofa-web-hub/ and /etc/sankofa-phoenix-api-hub/ per systemd examples; install systemd units from config/systemd/*.example (drop .example, adjust paths).
  4. Set .env overrides: IP_SANKOFA_WEB_HUB, SANKOFA_WEB_HUB_PORT, IP_SANKOFA_PHOENIX_API_HUB, SANKOFA_PHOENIX_API_HUB_PORT (see plan-sankofa-consolidated-hub-cutover.sh output after source scripts/lib/load-project-env.sh).
  5. Dry-run NPM upstream changes; then apply during a maintenance window. Confirm WebSocket (GraphQL subscriptions) through NPM if clients use graphql-ws.
  6. Smoke: curl -fsS http://<API_HUB>:<PORT>/health, GraphQL POST to /graphql, dbis_core health via hub as GET /api-docs or GET /health on upstream :3000 through /api/ only if mounted there — simplest: curl http://<hub>:<port>/api-docs (proxied) per NON_CHAIN_ECOSYSTEM_PLAN_REVIEW_AND_GAPS.md §2.4.
  7. Update inventory docs and VMID table; decommission retired CTs only after rollback window. Optionally bind Apollo to 127.0.0.1:4000 or firewall :4000 from LAN once NPM uses hub only (NON_CHAIN_ECOSYSTEM_PLAN_REVIEW_AND_GAPS.md §2.5).


8. Decision log (fill when adopted)

Decision Choice Date
Web hub pattern TBD (interim: discrete CTs; target: A / B / C)
API hub Tier 1 (nginx on VMID 7800, LAN 2026-04-13) 2026-04-13
Public API hostname phoenix.sankofa.nexus (NPM → 8080 hub; Apollo 127.0.0.1:4000) 2026-04-13
Retired VMIDs none