From 7f3dcf251368711588a897801dfa64271bf1ba95 Mon Sep 17 00:00:00 2001 From: defiQUG Date: Sun, 29 Mar 2026 13:41:02 -0700 Subject: [PATCH] feat(sankofa): public web CT 7806, portal NPM/DNS defaults, Keycloak redirect helper - Provision/sync scripts and systemd for corporate Next on 7806; IP_SANKOFA_PUBLIC_WEB for apex NPM - Portal stack: NEXTAUTH_URL default portal.sankofa.nexus; NPM fleet + migrate + DNS ordering - keycloak-sankofa-ensure-client-redirects.sh (KEYCLOAK_ADMIN_PASSWORD); .env.master.example hints - Docs: task list, inventory, FQDN/E2E/EXPECTED_WEB_CONTENT, AGENTS pointers Made-with: Cursor --- .env.master.example | 10 ++ AGENTS.md | 5 +- config/ip-addresses.conf | 11 ++ config/systemd/sankofa-public-web.service | 17 ++ docs/02-architecture/EXPECTED_WEB_CONTENT.md | 14 +- ..._PORTAL_ADMIN_ENDPOINT_CORRECTION_TASKS.md | 161 ++++++++++++++++++ docs/04-configuration/ALL_VMIDS_ENDPOINTS.md | 43 +++-- docs/04-configuration/E2E_ENDPOINTS_LIST.md | 5 +- .../04-configuration/FQDN_EXPECTED_CONTENT.md | 10 +- docs/MASTER_INDEX.md | 3 +- .../enable-sankofa-portal-login-7801.sh | 6 +- ...eycloak-sankofa-ensure-client-redirects.sh | 101 +++++++++++ .../provision-sankofa-public-web-lxc-7806.sh | 89 ++++++++++ .../sankofa-portal-ensure-nextauth-on-ct.sh | 2 +- .../deployment/sync-sankofa-portal-7801.sh | 10 +- .../sync-sankofa-public-web-to-ct.sh | 106 ++++++++++++ .../nginx-proxy-manager/migrate-to-npmplus.sh | 20 ++- .../update-npmplus-proxy-hosts-api.sh | 22 ++- scripts/update-all-dns-to-public-ip.sh | 4 + scripts/update-sankofa-npmplus-proxy-hosts.sh | 10 +- 20 files changed, 600 insertions(+), 49 deletions(-) create mode 100644 config/systemd/sankofa-public-web.service create mode 100644 docs/03-deployment/SANKOFA_PHOENIX_PUBLIC_PORTAL_ADMIN_ENDPOINT_CORRECTION_TASKS.md create mode 100755 scripts/deployment/keycloak-sankofa-ensure-client-redirects.sh create mode 100755 scripts/deployment/provision-sankofa-public-web-lxc-7806.sh create mode 100755 scripts/deployment/sync-sankofa-public-web-to-ct.sh diff --git a/.env.master.example b/.env.master.example index 2324673..b2c5586 100644 --- a/.env.master.example +++ b/.env.master.example @@ -52,6 +52,14 @@ NPMPLUS_ALLTRA_HYBX_VMID= IP_NPMPLUS_ALLTRA_HYBX= NPM_URL_MIFOS= +# --- Keycloak Admin API (optional) --- +# For scripts/deployment/keycloak-sankofa-ensure-client-redirects.sh — merge portal/admin redirect URIs. +# KEYCLOAK_URL=https://keycloak.sankofa.nexus +# KEYCLOAK_REALM=master +# KEYCLOAK_CLIENT_ID=sankofa-portal +# KEYCLOAK_ADMIN=admin +# KEYCLOAK_ADMIN_PASSWORD= + # --- Fastly --- FASTLY_API_TOKEN= @@ -159,6 +167,8 @@ SANKOFA_PHOENIX_API_URL= SANKOFA_PHOENIX_CLIENT_ID= SANKOFA_PHOENIX_CLIENT_SECRET= SANKOFA_PHOENIX_TENANT_ID= +# Corporate apex (sankofa.nexus) → CT 7806 when provisioned (default in ip-addresses stays portal until set) +# IP_SANKOFA_PUBLIC_WEB=192.168.11.63 # --- Frontend / MetaMask / Explorer --- VITE_WALLETCONNECT_PROJECT_ID= diff --git a/AGENTS.md b/AGENTS.md index 681d631..e5f81cc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,11 +21,14 @@ Orchestration for Proxmox VE, Chain 138 (`smom-dbis-138/`), explorers, NPMplus, | Submodule trees clean (CI / post-merge) | `bash scripts/verify/submodules-clean.sh` | | Submodule + explorer remotes | `docs/00-meta/SUBMODULE_HYGIENE.md` | | smom-dbis-138 `.env` in bash scripts | Prefer `source smom-dbis-138/scripts/lib/deployment/dotenv.sh` + `load_deployment_env --repo-root "$PROJECT_ROOT"` (trims RPC URL line endings). From an interactive shell: `source smom-dbis-138/scripts/load-env.sh`. Proxmox root scripts: `source scripts/lib/load-project-env.sh` (also trims common RPC vars). | -| Sankofa portal → CT 7801 (build + restart) | `./scripts/deployment/sync-sankofa-portal-7801.sh` (`--dry-run` first); sets `NEXTAUTH_URL` on CT via `sankofa-portal-ensure-nextauth-on-ct.sh` | +| Sankofa portal → CT 7801 (build + restart) | `./scripts/deployment/sync-sankofa-portal-7801.sh` (`--dry-run` first); default `NEXTAUTH_URL=https://portal.sankofa.nexus` via `sankofa-portal-ensure-nextauth-on-ct.sh` | +| Sankofa corporate web → CT 7806 | Provision: `./scripts/deployment/provision-sankofa-public-web-lxc-7806.sh`. Sync: `./scripts/deployment/sync-sankofa-public-web-to-ct.sh`. systemd: `config/systemd/sankofa-public-web.service`. Set `IP_SANKOFA_PUBLIC_WEB` in `.env`, then `scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh` | | CCIP relay (r630-01 host) | Unit: `config/systemd/ccip-relay.service` → `/etc/systemd/system/ccip-relay.service`; `systemctl enable --now ccip-relay` | | TsunamiSwap VM 5010 check | `./scripts/deployment/tsunamiswap-vm-5010-provision.sh` (inventory only until VM exists) | | The Order portal (`https://the-order.sankofa.nexus`) | OSJ management UI (secure auth); source repo **the_order** at `~/projects/the_order`. NPM upstream defaults to **order-haproxy** CT **10210** (`IP_ORDER_HAPROXY:80`); use `THE_ORDER_UPSTREAM_*` to point at the Sankofa portal if 10210 is down. Provision HAProxy: `scripts/deployment/provision-order-haproxy-10210.sh`. **`www.the-order.sankofa.nexus`** → **301** apex (same as www.sankofa / www.phoenix). | | Portal login + Keycloak systemd + `.env` (prints password once) | `./scripts/deployment/enable-sankofa-portal-login-7801.sh` (`--dry-run` first) | +| Keycloak redirect URIs (portal + admin) | `./scripts/deployment/keycloak-sankofa-ensure-client-redirects.sh` — needs `KEYCLOAK_ADMIN_PASSWORD` in `.env` | +| NPM TLS for hosts missing certs | `./scripts/request-npmplus-certificates.sh` — optional `CERT_DOMAINS_FILTER='portal\\.sankofa|admin\\.sankofa'` | | Completable (no LAN) | `./scripts/run-completable-tasks-from-anywhere.sh` | | Operator (LAN + secrets) | `./scripts/run-all-operator-tasks-from-lan.sh` (use `--skip-backup` if `NPM_PASSWORD` unset) | | Cloudflare bulk DNS → `PUBLIC_IP` | `./scripts/update-all-dns-to-public-ip.sh` — use **`--dry-run`** and **`--zone-only=sankofa.nexus`** (or `d-bis.org` / `mim4u.org` / `defi-oracle.io`) to limit scope; see script header. Prefer scoped **`CLOUDFLARE_API_TOKEN`** (see `.env.master.example`). | diff --git a/config/ip-addresses.conf b/config/ip-addresses.conf index 5838c87..e358655 100644 --- a/config/ip-addresses.conf +++ b/config/ip-addresses.conf @@ -177,6 +177,17 @@ SANKOFA_PHOENIX_API_PORT="${SANKOFA_PHOENIX_API_PORT:-4000}" SANKOFA_PORTAL_PORT="${SANKOFA_PORTAL_PORT:-3000}" IP_SANKOFA_PHOENIX_API="${IP_SANKOFA_PHOENIX_API:-$IP_SERVICE_50}" IP_SANKOFA_PORTAL="${IP_SANKOFA_PORTAL:-$IP_SERVICE_51}" +# Corporate apex (sankofa.nexus marketing). Default: same as portal until you set IP_SANKOFA_PUBLIC_WEB in .env (e.g. CT 7806). +# CT 7806 (sankofa-public-web) LAN IP when provisioned — see scripts/deployment/provision-sankofa-public-web-lxc-7806.sh +IP_SANKOFA_PUBLIC_WEB_CT="${IP_SANKOFA_PUBLIC_WEB_CT:-192.168.11.63}" +IP_SANKOFA_PUBLIC_WEB="${IP_SANKOFA_PUBLIC_WEB:-$IP_SANKOFA_PORTAL}" +SANKOFA_PUBLIC_WEB_PORT="${SANKOFA_PUBLIC_WEB_PORT:-$SANKOFA_PORTAL_PORT}" +# Client SSO apps (portal.sankofa.nexus, admin.sankofa.nexus) — typical: same LXC as hybrid portal (7801). +IP_SANKOFA_CLIENT_SSO="${IP_SANKOFA_CLIENT_SSO:-$IP_SANKOFA_PORTAL}" +SANKOFA_CLIENT_SSO_PORT="${SANKOFA_CLIENT_SSO_PORT:-$SANKOFA_PORTAL_PORT}" +# Operator dash (dash.sankofa.nexus). Leave unset to skip creating/updating dash in NPM fleet script until provisioned. +# IP_SANKOFA_DASH="192.168.11.xx" +# SANKOFA_DASH_PORT="${SANKOFA_DASH_PORT:-3000}" # Gov Portals dev (VMID 7804) — DBIS, ICCC, OMNL, XOM at *.xom-dev.phoenix.sankofa.nexus IP_GOV_PORTALS_DEV="192.168.11.54" diff --git a/config/systemd/sankofa-public-web.service b/config/systemd/sankofa-public-web.service new file mode 100644 index 0000000..927544f --- /dev/null +++ b/config/systemd/sankofa-public-web.service @@ -0,0 +1,17 @@ +[Unit] +Description=Sankofa corporate public web (Next.js root app) for sankofa.nexus +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/sankofa-public-web +Environment=NODE_ENV=production +Environment=PORT=3000 +# Use login shell so corepack/pnpm PATH matches interactive CT admin. +ExecStart=/bin/bash -lc 'cd /opt/sankofa-public-web && exec pnpm start' +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/docs/02-architecture/EXPECTED_WEB_CONTENT.md b/docs/02-architecture/EXPECTED_WEB_CONTENT.md index 6a195b4..3ee79db 100644 --- a/docs/02-architecture/EXPECTED_WEB_CONTENT.md +++ b/docs/02-architecture/EXPECTED_WEB_CONTENT.md @@ -1,7 +1,7 @@ # Web Properties — Ground Truth & Validation -**Last Updated:** 2026-03-27 -**Document Version:** 1.5 +**Last Updated:** 2026-03-29 +**Document Version:** 1.6 **Status:** Active Documentation --- @@ -314,17 +314,17 @@ Backend (typical): | Service | Domain | VMID | IP | Port | Status | Access model | |---------|--------|------|-----|------|--------|----------------| | **Phoenix** (API today; division hostname) | phoenix.sankofa.nexus | 7800 | 192.168.11.50 | 4000 | ✅ Active | Public web **intent**; API paths coexist | -| **Sankofa public web** | sankofa.nexus | 7801 | 192.168.11.51 | 3000 | ✅ Active | Public **intent** (see hostname model) | +| **Sankofa public web** | sankofa.nexus | **7806** @ `192.168.11.63` (or **`IP_SANKOFA_PUBLIC_WEB`** in `.env`) | **`IP_SANKOFA_PUBLIC_WEB`** | **`SANKOFA_PUBLIC_WEB_PORT`** (`3000`) | ✅ Active | Provision: `provision-sankofa-public-web-lxc-7806.sh`; sync: `sync-sankofa-public-web-to-ct.sh`; NPM fleet script | | **The Order (edge)** | the-order.sankofa.nexus | 10210 → 7801 | 192.168.11.39:80 → .51:3000 | 80 → 3000 | ✅ Active | HAProxy then portal; see §2b | | **Sankofa Studio** | studio.sankofa.nexus | 7805 | 192.168.11.72 | 8000 | ✅ Active | `/studio/` | | **Keycloak IdP** | keycloak.sankofa.nexus | 7802 | (see ALL_VMIDS) | 8080 | ✅ Active | IdP + `/admin` | -| **Client admin (SSO)** | admin.sankofa.nexus | — | — | — | 🔶 **Intent** — NPM + app upstream not pinned in VM inventory; may share portal stack (**7801**) until split (see §4, Open Decisions §4) | SSO | -| **Client portal (SSO)** | portal.sankofa.nexus | **7801** (typical) | 192.168.11.51 | 3000 | ✅ **Active** when NPM routes this hostname to the Sankofa portal stack; `NEXTAUTH_URL` / public OIDC URL per `scripts/deployment/sync-sankofa-portal-7801.sh` | SSO | -| **Operator dash** | dash.sankofa.nexus | — | — | — | 🔶 **Intent** — IP allowlist + system auth + MFA; **VMID/IP not fixed** in this matrix until NPM/upstream is wired (see §6) | IP + MFA | +| **Client admin (SSO)** | admin.sankofa.nexus | **7801** (typical) | 192.168.11.51 | 3000 | ✅ **NPM row** in fleet script (shares **`IP_SANKOFA_CLIENT_SSO`** with portal until split) | SSO | +| **Client portal (SSO)** | portal.sankofa.nexus | **7801** (typical) | 192.168.11.51 | 3000 | ✅ **NPM row** in fleet script; default **`NEXTAUTH_URL=https://portal.sankofa.nexus`** (`sync-sankofa-portal-7801.sh`) | SSO | +| **Operator dash** | dash.sankofa.nexus | — | **`IP_SANKOFA_DASH`** (when set) | **`SANKOFA_DASH_PORT`** | 🔶 **Fleet script** adds NPM upstream only when **`IP_SANKOFA_DASH`** set; app + IP allowlist + MFA still operator work (see §6) | IP + MFA | | **SolaceScanScout** | explorer.d-bis.org | 5000 | 192.168.11.140 | 80/4000 | ✅ Active | Public | | **Blockscout (generic hostname)** | blockscout.defi-oracle.io | **5000** | 192.168.11.140 | **80** (TLS at NPM) | ✅ **Active** when NPM proxies here; **same class** of Blockscout UI as §7 but **not** canonical **SolaceScanScout / Chain 138** branding (see §8) | Public | -**Table notes:** `admin` / `dash` rows stay **non-numeric** on VMID until inventory and NPM proxy rows are authoritative in [ALL_VMIDS_ENDPOINTS.md](../04-configuration/ALL_VMIDS_ENDPOINTS.md) and your NPM export. **`blockscout.defi-oracle.io`** has been documented in routing summaries as terminating on **VMID 5000** (`192.168.11.140:80`); confirm live NPM if behavior differs. +**Table notes:** `dash` **VMID** stays **TBD** until an operator app is pinned; set **`IP_SANKOFA_DASH`** in `config/ip-addresses.conf` to create/update the NPM proxy via `update-npmplus-proxy-hosts-api.sh`. **`admin`** / **`portal`** share **7801** in the default config. Confirm live NPM + DNS + Keycloak redirect URIs after each change. **`blockscout.defi-oracle.io`**: see [ALL_VMIDS_ENDPOINTS.md](../04-configuration/ALL_VMIDS_ENDPOINTS.md). --- diff --git a/docs/03-deployment/SANKOFA_PHOENIX_PUBLIC_PORTAL_ADMIN_ENDPOINT_CORRECTION_TASKS.md b/docs/03-deployment/SANKOFA_PHOENIX_PUBLIC_PORTAL_ADMIN_ENDPOINT_CORRECTION_TASKS.md new file mode 100644 index 0000000..c88601f --- /dev/null +++ b/docs/03-deployment/SANKOFA_PHOENIX_PUBLIC_PORTAL_ADMIN_ENDPOINT_CORRECTION_TASKS.md @@ -0,0 +1,161 @@ +# Sankofa and Phoenix — public, portal, and system admin endpoint correction tasks + +**Created:** 2026-03-29 +**Purpose:** Ordered checklist to align **DNS, NPM upstreams, deployed apps, auth URLs, and inventory** with the hostname model in [EXPECTED_WEB_CONTENT.md](../02-architecture/EXPECTED_WEB_CONTENT.md) and [FQDN_EXPECTED_CONTENT.md](../04-configuration/FQDN_EXPECTED_CONTENT.md). +**Verify:** [E2E_ENDPOINTS_LIST.md](../04-configuration/E2E_ENDPOINTS_LIST.md), `scripts/verify/verify-end-to-end-routing.sh --profile=public` (from LAN). + +--- + +## Target state (summary) + +| Hostname | Tier | Expected content | +|----------|------|------------------| +| `sankofa.nexus` / `www` | Public web | Corporate / brand (Sankofa — Sovereign Technologies), unauthenticated marketing pages. | +| `phoenix.sankofa.nexus` / `www` | Public web (+ API) | Phoenix Cloud Services division web (intent); API paths may coexist until split. | +| `portal.sankofa.nexus` | Client SSO | Hybrid cloud / client workspace, marketplace, NextAuth + Keycloak with **public URL = this host**. | +| `admin.sankofa.nexus` | Client SSO | Client access administration (IAM-style); not Keycloak operator `/admin`. | +| `keycloak.sankofa.nexus` | IdP | Realm login, tokens, well-known; operator console at `/admin`. | +| `dash.sankofa.nexus` | Operator / systems | IP allowlist + system auth + MFA; not client self-service. | +| `the-order.sankofa.nexus` | Program portal | OSJ / Order app; edge 10210 (HAProxy) → appropriate upstream. | +| `studio.sankofa.nexus` | Tooling | Sankofa Studio (7805), `/studio/`. | + +**Remaining operator work:** Add **DNS A/AAAA** records for `portal.sankofa.nexus` and `admin.sankofa.nexus` (and `dash` when used). Run **`scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh`** from LAN with `NPM_PASSWORD`, assign **SSL** in NPM for new rows, and update **Keycloak** client redirect URIs for `https://portal.sankofa.nexus/*` (and `admin` if distinct). Provision **CT 7806** (or set **`IP_SANKOFA_PUBLIC_WEB`**) when the corporate site should no longer share the portal binary on apex. + +--- + +## Implementation status (repo — 2026-03-29) + +| Item | Done in repo | +|------|----------------| +| NPM fleet script: `portal` / `admin` / conditional `dash` | `scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh` | +| NPM migrate script + Sankofa ID script | `migrate-to-npmplus.sh`, `update-sankofa-npmplus-proxy-hosts.sh` (apex uses `IP_SANKOFA_PUBLIC_WEB`) | +| Config vars | `config/ip-addresses.conf`: `IP_SANKOFA_PUBLIC_WEB`, `SANKOFA_PUBLIC_WEB_PORT`, `IP_SANKOFA_CLIENT_SSO`, `SANKOFA_CLIENT_SSO_PORT`, optional `IP_SANKOFA_DASH` | +| Default `NEXTAUTH_URL` for portal | `sync-sankofa-portal-7801.sh`, `sankofa-portal-ensure-nextauth-on-ct.sh`, `enable-sankofa-portal-login-7801.sh` → **`https://portal.sankofa.nexus`** | +| Corporate site deploy path | `scripts/deployment/sync-sankofa-public-web-to-ct.sh` + `config/systemd/sankofa-public-web.service` | +| Inventory / index | `ALL_VMIDS_ENDPOINTS.md`, `AGENTS.md`, `MASTER_INDEX.md` (task list row) | + +**Not automatable here:** Keycloak **realm client** redirect URI edits (operator console), provisioning CT **7806** for corporate apex split, Phoenix marketing page on **7800**, operator **dash** app + **`IP_SANKOFA_DASH`**, NPM **IP allowlists** for dash, stakeholder sign-off. + +**Operator run (2026-03-29, LAN):** Loaded `.env` (`NPM_PASSWORD`, Cloudflare). Ran `update-npmplus-proxy-hosts-api.sh` (added NPM rows **portal** / **admin**). Ran `sankofa-portal-ensure-nextauth-on-ct.sh` + full `sync-sankofa-portal-7801.sh`. Ran `update-all-dns-to-public-ip.sh --zone-only=sankofa.nexus` (new A records **portal**, **admin**, **keycloak**; **studio** updated). E2E `verify-end-to-end-routing.sh --profile=public` exit `0`; evidence: `docs/04-configuration/verification-evidence/e2e-verification-20260329_045318/` (and `045210/`). **7806** provisioned **2026-03-29** on r630-01 @ **192.168.11.63**; corporate site synced; NPM **sankofa.nexus** / **www** → **.63:3000** when `.env` sets **`IP_SANKOFA_PUBLIC_WEB=192.168.11.63`** (operator machine). Ran `request-npmplus-certificates.sh` with `CERT_DOMAINS_FILTER` for **portal.sankofa.nexus** and **admin.sankofa.nexus**; NPM certificate IDs **180** / **179** assigned with **SSL forced**; HTTPS smoke **200**. **Keycloak:** set `KEYCLOAK_ADMIN_PASSWORD` in `.env` and run `scripts/deployment/keycloak-sankofa-ensure-client-redirects.sh`, or add **Valid redirect URIs** / **Web origins** in the console for **portal** and **admin** (client **sankofa-portal**, realm **master** per portal `.env` template). + +--- + +## Phase 0 — Decisions and inventory (blockers) + +1. **Confirm split strategy for 7801** + - **Option A:** Second CT/VM (new VMID) for **public marketing** Next app; keep **7801** for **`portal`/`admin`** only. + - **Option B:** Single process with host-based routing (middleware) — higher risk; only if product accepts shared runtime. + - Document chosen VMID(s), ports, and systemd unit names in [ALL_VMIDS_ENDPOINTS.md](../04-configuration/ALL_VMIDS_ENDPOINTS.md). + +2. **Resolve Phoenix “public web vs API”** (open decision in EXPECTED_WEB_CONTENT) + - Either add **static/marketing** upstream or **path-based** split on 7800, or dedicate a small VM for Phoenix marketing. + - Record **browser-default** behavior for `GET /` on `phoenix.sankofa.nexus` (not only `/health` / GraphQL). + +3. **Pin `admin` and `dash` targets** + - **admin:** same app as portal (path prefix), separate deploy, or separate repo — decide and record IP:port / VMID. + - **dash:** choose app (existing vs greenfield), VMID, IP:port, MFA method, and **NPM IP allowlist** / VPN-only DNS policy. + +4. **Keycloak clients** + - For each public hostname that uses OIDC (`portal`, `admin`, and any others), register **valid redirect URIs** and **web origins** for the **final** URLs (not interim apex). + +--- + +## Phase 1 — Sankofa public apex (`sankofa.nexus`) + +5. **Build and deploy corporate site** from **`Sankofa` repo root** (not `Sankofa/portal`): `src/app`, `public/`, root `package.json` / `next.config.js`. +6. **Provision runtime** on the chosen host (new CT or existing): Node/pnpm, systemd (or container), production `pnpm build` + `pnpm start`, listen port (e.g. 3000). +7. **Point NPM** `sankofa.nexus` and `www.sankofa.nexus` → **new public-site upstream** (update `update-npmplus-proxy-hosts-api.sh`, `update-sankofa-npmplus-proxy-hosts.sh`, `migrate-to-npmplus.sh` as needed). +8. **Remove portal-only behavior from apex** (no forced login shell on `/` for anonymous users unless product explicitly wants a teaser). +9. **Smoke test:** anonymous `GET https://sankofa.nexus/` shows marketing; links to `phoenix`, `portal`, `the-order` work. + +--- + +## Phase 2 — Client portal (`portal.sankofa.nexus`) + +10. **Ensure NPM proxy host** `portal.sankofa.nexus` exists and points to **portal** upstream (today typically **192.168.11.51:3000** / 7801 if portal stays there). Add lines to `update-npmplus-proxy-hosts-api.sh` (and sibling updaters). +11. **Set NextAuth / OIDC public URL** to `https://portal.sankofa.nexus`: + - `SANKOFA_PORTAL_NEXTAUTH_URL=https://portal.sankofa.nexus` when running `sync-sankofa-portal-7801.sh` and `sankofa-portal-ensure-nextauth-on-ct.sh`. + - Update defaults in script headers/comments so operators are not steered to apex. +12. **Update `Sankofa/portal`** `.env.example` / README: replace `NEXTAUTH_URL=https://sankofa.nexus` with **`https://portal.sankofa.nexus`**. +13. **Keycloak:** client `redirect_uri` / `Valid Redirect URIs` must include `https://portal.sankofa.nexus/*` (and dev hosts if any). +14. **Cookie / CSRF:** confirm `NEXTAUTH_URL` matches browser bar; fix any `localhost` or apex leakage in callbacks. + +--- + +## Phase 3 — Client admin (`admin.sankofa.nexus`) + +15. **Implement or wire** the admin UI (if not already a route in portal app, add app or path and deploy target). +16. **NPM:** add `admin.sankofa.nexus` → upstream (may equal portal VM until split). +17. **OIDC:** separate Keycloak client or fine-grained roles; redirect URIs for `https://admin.sankofa.nexus/*`. +18. **Document** in ALL_VMIDS_ENDPOINTS + EXPECTED_WEB_CONTENT deployment table (replace “intent / TBD” with IP:port). + +--- + +## Phase 4 — Operator systems admin (`dash.sankofa.nexus`) + +19. **Deploy** dash application to chosen VMID with **MFA** and **strong session** policy. +20. **NPM:** create `dash.sankofa.nexus` proxy; attach **IP allowlist** (NPM access list / firewall) per EXPECTED_WEB_CONTENT. +21. **DNS:** restrict resolution to operator networks if policy requires (optional; often IP block at edge is enough). +22. **Inventory:** pin VMID, IP, port in ALL_VMIDS_ENDPOINTS and FQDN_EXPECTED_CONTENT. + +--- + +## Phase 5 — Phoenix public (`phoenix.sankofa.nexus`) + +23. **Deliver browser-facing division home** (or documented interim: redirect to docs / status page) consistent with FQDN_EXPECTED_CONTENT. +24. **Preserve API** (`/health`, `/graphql`, etc.) for monitors and clients; avoid breaking E2E `/health` checks. +25. **NPM / headers:** ensure WebSocket or long-lived connections if GraphQL subscriptions are exposed on same host. +26. **Copy and nav:** corporate site links “Phoenix” to this hostname, not to portal. + +--- + +## Phase 6 — Program and tooling hostnames (sanity) + +27. **the-order.sankofa.nexus:** confirm HAProxy **10210** → correct upstream; if Order app diverges from generic portal, point backend to **the_order** build instead of 7801 generic portal (per product). +28. **studio.sankofa.nexus:** confirm 7805 `/studio/`; certificate and upstream unchanged unless Studio moves. +29. **www.*:** confirm **301** to apex for `sankofa`, `phoenix`, `the-order` in NPM `advanced_config`. + +--- + +## Phase 7 — Automation, docs, and regression + +30. ~~**Scripts:** extend fleet NPM updater~~ **Done:** `update-npmplus-proxy-hosts-api.sh`, `migrate-to-npmplus.sh`, `update-sankofa-npmplus-proxy-hosts.sh` + `config/ip-addresses.conf`. +31. ~~**Corporate sync script**~~ **Done:** `scripts/deployment/sync-sankofa-public-web-to-ct.sh` + `config/systemd/sankofa-public-web.service`. +32. **E2E:** after DNS + NPM SSL for `portal` / `admin` (and `dash` when live), move hosts from “optional when fail” to **required** in `verify-end-to-end-routing.sh` / tighten `E2E_OPTIONAL_WHEN_FAIL`. +33. ~~**EXPECTED_WEB_CONTENT.md** table~~ **Partially done:** `admin` / `portal` NPM rows documented; **`dash`** stays 🔶 until app + **`IP_SANKOFA_DASH`**; apex row notes **7806** / `IP_SANKOFA_PUBLIC_WEB`. +34. **Run** `verify-end-to-end-routing.sh --profile=public` from LAN; archive evidence under `docs/04-configuration/verification-evidence/`. +35. **Stakeholder sign-off:** security review of public vs SSO vs operator boundaries and Keycloak client surface area. + +--- + +## Quick dependency graph + +```mermaid +flowchart LR + D0[Phase 0 decisions] --> P1[Phase 1 apex public] + D0 --> P2[Phase 2 portal host] + D0 --> P3[Phase 3 admin] + D0 --> P4[Phase 4 dash] + D0 --> P5[Phase 5 Phoenix web] + P2 --> KC[Keycloak client URIs] + P3 --> KC + P1 --> NPM[NPM / DNS updates] + P2 --> NPM + P3 --> NPM + P4 --> NPM + P5 --> NPM + NPM --> E2E[Phase 7 E2E + docs] +``` + +--- + +## References (repo) + +- Hostname model: [EXPECTED_WEB_CONTENT.md](../02-architecture/EXPECTED_WEB_CONTENT.md) +- FQDN matrix: [FQDN_EXPECTED_CONTENT.md](../04-configuration/FQDN_EXPECTED_CONTENT.md) +- IPs / VMIDs: [ALL_VMIDS_ENDPOINTS.md](../04-configuration/ALL_VMIDS_ENDPOINTS.md) +- NPM API updater: `scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh` +- Portal deploy: `scripts/deployment/sync-sankofa-portal-7801.sh`, `scripts/deployment/sankofa-portal-ensure-nextauth-on-ct.sh` +- Corporate web deploy: `scripts/deployment/sync-sankofa-public-web-to-ct.sh` +- Order edge: `scripts/deployment/provision-order-haproxy-10210.sh` +- Source trees (operator workstation): `../Sankofa` (public site root), `../Sankofa/portal` (client portal), `~/projects/the_order` (Order UI) diff --git a/docs/04-configuration/ALL_VMIDS_ENDPOINTS.md b/docs/04-configuration/ALL_VMIDS_ENDPOINTS.md index 05a31b8..59a0135 100644 --- a/docs/04-configuration/ALL_VMIDS_ENDPOINTS.md +++ b/docs/04-configuration/ALL_VMIDS_ENDPOINTS.md @@ -1,6 +1,6 @@ # Complete VMID and Endpoints Reference -**Last Updated:** 2026-03-26 +**Last Updated:** 2026-03-29 **Document Version:** 1.2 **Status:** Active Documentation — **Master (source of truth)** for VMID, IP, port, and domain mapping. See [MASTER_DOCUMENTATION_INDEX.md](../00-meta/MASTER_DOCUMENTATION_INDEX.md). @@ -211,6 +211,22 @@ The following VMIDs have been permanently removed: --- +### Hyperledger Aries / AnonCreds + +| VMID | IP Address | Hostname | Status | Endpoints | Purpose | +|------|------------|----------|--------|-----------|---------| +| 6500 | 192.168.11.88 | aries-1 | ✅ Running | ACA-Py DIDComm: 8030, Admin API: 8031 | Hyperledger Aries / AnonCreds agent runtime | + +--- + +### Hyperledger Caliper + +| VMID | IP Address | Hostname | Status | Endpoints | Purpose | +|------|------------|----------|--------|-----------|---------| +| 6600 | 192.168.11.93 | caliper-1 | ✅ Running | Local CLI workspace, outbound RPC to 192.168.11.211:8545 / 8546 | Hyperledger Caliper benchmark harness | + +--- + ### DBIS Core Services | VMID | IP Address | Hostname | Status | Endpoints | Purpose | @@ -256,15 +272,17 @@ The following VMIDs have been permanently removed: | VMID | IP Address | Hostname | Status | Endpoints | Purpose | |------|------------|----------|--------|-----------|---------| | 7800 | 192.168.11.50 | sankofa-api-1 | ✅ Running | GraphQL: 4000, Health: /health | Phoenix API (Cloud Platform Portal) | -| 7801 | 192.168.11.51 | sankofa-portal-1 | ✅ Running | Web: 3000 | Sankofa Portal (Company Website) | +| 7801 | 192.168.11.51 | sankofa-portal-1 | ✅ Running | Web: 3000 | Hybrid cloud **client portal** (`portal.sankofa.nexus` / `admin.sankofa.nexus` when NPM routes); not the long-term corporate apex app — see `IP_SANKOFA_PUBLIC_WEB` / `sync-sankofa-public-web-to-ct.sh` | | 7802 | 192.168.11.52 | sankofa-keycloak-1 | ✅ Running | Keycloak: 8080, Admin: /admin | Identity and Access Management | | 7803 | 192.168.11.53 | sankofa-postgres-1 | ✅ Running | PostgreSQL: 5432 | Database Service | | 7804 | 192.168.11.54 | (Gov Portals dev) | ✅ Running | Web: 80 | Gov Portals — DBIS, ICCC, OMNL, XOM (*.xom-dev.phoenix.sankofa.nexus) | | 7805 | 192.168.11.72 | sankofa-studio | — | API: 8000 | Sankofa Studio (FusionAI Creator) — studio.sankofa.nexus (IP .72; .55 = VMID 10230 order-vault) | +| 7806 | 192.168.11.63 | sankofa-public-web | ✅ Running | Web: 3000 | Corporate / marketing Next.js (Sankofa **repo root**); provision: `scripts/deployment/provision-sankofa-public-web-lxc-7806.sh`; deploy: `scripts/deployment/sync-sankofa-public-web-to-ct.sh`; NPM apex via **`IP_SANKOFA_PUBLIC_WEB`** (`.env` or override) | **Public Domains** (NPMplus routing): -- `sankofa.nexus` → Routes to `http://192.168.11.51:3000` (Sankofa Portal/VMID 7801) ✅ -- `www.sankofa.nexus` → Same upstream as apex; NPM **`advanced_config`** issues **301** to **`https://sankofa.nexus`** (preserve path/query via `$request_uri`). ✅ +- `sankofa.nexus` / `www.sankofa.nexus` → **`IP_SANKOFA_PUBLIC_WEB`:`SANKOFA_PUBLIC_WEB_PORT** (typical: **7806** `192.168.11.63:3000` when `.env` sets `IP_SANKOFA_PUBLIC_WEB`; else defaults to portal **7801**); fleet script: `scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh`. **`www`** → **301** → apex `https://sankofa.nexus` (`$request_uri`). ✅ +- `portal.sankofa.nexus` / `admin.sankofa.nexus` → **`IP_SANKOFA_CLIENT_SSO`:`SANKOFA_CLIENT_SSO_PORT** (typical: 7801 `:3000`). NextAuth / OIDC public URL: **`https://portal.sankofa.nexus`**. ✅ when NPM proxy rows exist (fleet script creates/updates them). +- `dash.sankofa.nexus` → Set **`IP_SANKOFA_DASH`** (+ `SANKOFA_DASH_PORT`) in `config/ip-addresses.conf` to enable upstream in the fleet script; IP allowlist at NPM is operator policy. 🔶 until dash app + env are set. - `phoenix.sankofa.nexus` → Routes to `http://192.168.11.50:4000` (Phoenix API/VMID 7800) ✅ - `www.phoenix.sankofa.nexus` → Same upstream; **301** to **`https://phoenix.sankofa.nexus`**. ✅ - `the-order.sankofa.nexus` / `www.the-order.sankofa.nexus` → OSJ management portal (secure auth). App source: **the_order** at `~/projects/the_order`. NPMplus default upstream: **order-haproxy** `http://192.168.11.39:80` (VMID **10210**), which proxies to Sankofa portal `http://192.168.11.51:3000` (7801). Fallback: set `THE_ORDER_UPSTREAM_IP` / `THE_ORDER_UPSTREAM_PORT` to `.51` / `3000` if HAProxy is offline. **`www.the-order.sankofa.nexus`** → **301** **`https://the-order.sankofa.nexus`** (same as `www.sankofa` / `www.phoenix`). @@ -520,8 +538,11 @@ This section lists all endpoints that should be configured in NPMplus, extracted | `secure.mim4u.org` | `192.168.11.37` | `http` | `80` | ❌ No | MIM4U Secure Portal (VMID 7810) | | `training.mim4u.org` | `192.168.11.37` | `http` | `80` | ❌ No | MIM4U Training Portal (VMID 7810) | | **Sankofa Phoenix Services** | -| `sankofa.nexus` | `192.168.11.51` | `http` | `3000` | ❌ No | Sankofa Portal - Company Website (VMID 7801) ✅ **Deployed** | -| `www.sankofa.nexus` | `192.168.11.51` | `http` | `3000` | ❌ No | Sankofa Portal (VMID 7801) ✅ **Deployed** | +| `sankofa.nexus` | **`IP_SANKOFA_PUBLIC_WEB`** (default `.51` until public-web CT) | `http` | **`SANKOFA_PUBLIC_WEB_PORT`** (`3000`) | ❌ No | Corporate apex; fleet script `update-npmplus-proxy-hosts-api.sh` | +| `www.sankofa.nexus` | same as apex | `http` | same | ❌ No | **301** → `https://sankofa.nexus` | +| `portal.sankofa.nexus` | **`IP_SANKOFA_CLIENT_SSO`** (typ. `.51` / 7801) | `http` | **`SANKOFA_CLIENT_SSO_PORT`** (`3000`) | ❌ No | Client SSO portal; `NEXTAUTH_URL=https://portal.sankofa.nexus` | +| `admin.sankofa.nexus` | same as portal | `http` | same | ❌ No | Client access admin (same upstream until split) | +| `dash.sankofa.nexus` | **`IP_SANKOFA_DASH`** (set in `ip-addresses.conf`) | `http` | **`SANKOFA_DASH_PORT`** | ❌ No | Operator dash — row omitted from fleet script until `IP_SANKOFA_DASH` set | | `phoenix.sankofa.nexus` | `192.168.11.50` | `http` | `4000` | ❌ No | Phoenix API - Cloud Platform Portal (VMID 7800) ✅ **Deployed** | | `www.phoenix.sankofa.nexus` | `192.168.11.50` | `http` | `4000` | ❌ No | Phoenix API (VMID 7800) ✅ **Deployed** | | `the-order.sankofa.nexus`, `www.the-order.sankofa.nexus` | `192.168.11.39` (10210 HAProxy; default) or `192.168.11.51` (direct portal if env override) | `http` | `80` or `3000` | ❌ No | NPM → **.39:80** by default; HAProxy → **.51:3000** | @@ -537,9 +558,7 @@ Some domains use path-based routing in NPM configs: - `/graph` → `http://192.168.11.155:3000` (DBIS GraphQL) - `/` → `http://192.168.11.130:80` (DBIS Frontend) -**`sankofa.nexus`** (per deploy script): -- `/api` → `http://10.160.0.10:4000` (Sankofa API) -- `/` → `http://10.160.0.11:3000` (Sankofa Portal) +**`sankofa.nexus`** (intent): corporate marketing at **`IP_SANKOFA_PUBLIC_WEB`**; **`portal.sankofa.nexus`** serves the authenticated portal at **`IP_SANKOFA_CLIENT_SSO`**. Legacy path-based splits (if any) should be reconciled with [EXPECTED_WEB_CONTENT.md](../02-architecture/EXPECTED_WEB_CONTENT.md). **Note**: NPMplus may need custom location blocks or separate proxy hosts for path-based routing. @@ -550,7 +569,9 @@ Some domains use path-based routing in NPM configs: | Domain | Correct target (VMID, IP:port) | Do NOT point to | |--------|--------------------------------|-----------------| | `explorer.d-bis.org` | 5000, 192.168.11.140:80 (web), :4000 (API) | — | -| `sankofa.nexus`, `www.sankofa.nexus` | 7801, 192.168.11.51:3000 | 192.168.11.140 (Blockscout) | +| `sankofa.nexus`, `www.sankofa.nexus` | **Public web:** target **7806** (or `IP_SANKOFA_PUBLIC_WEB`) when split; defaults still **7801**, 192.168.11.51:3000 | 192.168.11.140 (Blockscout) | +| `portal.sankofa.nexus`, `admin.sankofa.nexus` | **7801**, 192.168.11.51:3000 (`IP_SANKOFA_CLIENT_SSO`) | 192.168.11.140 (Blockscout) | +| `dash.sankofa.nexus` | Set **`IP_SANKOFA_DASH`** when operator dash exists | 192.168.11.140 (Blockscout) | | `phoenix.sankofa.nexus`, `www.phoenix.sankofa.nexus` | 7800, 192.168.11.50:4000 | 192.168.11.140 (Blockscout) | | `the-order.sankofa.nexus`, `www.the-order.sankofa.nexus` | 10210, 192.168.11.39:80 | 192.168.11.140 (Blockscout) | | `studio.sankofa.nexus` | 7805, 192.168.11.72:8000 | — | @@ -561,7 +582,7 @@ If NPMplus proxy hosts for sankofa.nexus or phoenix.sankofa.nexus currently poin --- -**Last Updated**: 2026-03-27 +**Last Updated**: 2026-03-29 **Maintained By**: Infrastructure Team --- diff --git a/docs/04-configuration/E2E_ENDPOINTS_LIST.md b/docs/04-configuration/E2E_ENDPOINTS_LIST.md index e48f82f..9f413b7 100644 --- a/docs/04-configuration/E2E_ENDPOINTS_LIST.md +++ b/docs/04-configuration/E2E_ENDPOINTS_LIST.md @@ -9,7 +9,8 @@ **What each hostname should present (operator narrative):** [FQDN_EXPECTED_CONTENT.md](FQDN_EXPECTED_CONTENT.md). -**Latest verified public pass:** `2026-03-27` via `bash scripts/verify/verify-end-to-end-routing.sh --profile=public` with report at [verification_report.md](verification-evidence/e2e-verification-20260327_134032/verification_report.md). Result: exit `0`, `DNS passed: 38`, `Failed: 0`, `HTTPS passed: 19`, `Skipped / optional: 1` (after `run-all-operator-tasks-from-lan.sh` NPM sync; `rpc.defi-oracle.io` may log HTTP 405 on the verifier probe but stays non-failing for the profile). +**Latest verified public pass:** `2026-03-29` via `bash scripts/verify/verify-end-to-end-routing.sh --profile=public` with report at [verification_report.md](verification-evidence/e2e-verification-20260329_045318/verification_report.md). Result: exit `0`, `DNS passed: 42`, `Failed: 0`, `HTTPS passed: 27`, `Skipped / optional: 2` (NPM fleet + sankofa zone DNS for `portal` / `admin` / `keycloak`, portal sync + NextAuth on 7801). +**Earlier same day:** [verification_report.md](verification-evidence/e2e-verification-20260329_045210/verification_report.md). **Previous:** `2026-03-27` — [verification_report.md](verification-evidence/e2e-verification-20260327_134032/verification_report.md). **Latest verified private/admin pass:** `2026-03-27` via `bash scripts/verify/verify-end-to-end-routing.sh --profile=private` with report at [verification_report.md](verification-evidence/e2e-verification-20260327_134137/verification_report.md). Result: exit `0`, `DNS passed: 4`, `Failed: 0`. **Evidence folders:** Each run creates `verification-evidence/e2e-verification-YYYYMMDD_HHMMSS/`. Commit the runs you want on record; older dirs can be removed locally to reduce noise (`scripts/maintenance/prune-e2e-verification-evidence.sh --dry-run` lists candidates). Routing truth is **not** inferred from old reports—use [ALL_VMIDS_ENDPOINTS.md](ALL_VMIDS_ENDPOINTS.md). @@ -41,7 +42,7 @@ | studio.sankofa.nexus | web | https://studio.sankofa.nexus | Sankofa Studio (FusionAI Creator) at VMID 7805. | | keycloak.sankofa.nexus | web | https://keycloak.sankofa.nexus | Keycloak IdP (VMID 7802); client SSO for admin/portal. | | admin.sankofa.nexus | web | https://admin.sankofa.nexus | Client SSO: access administration (hostname intent; NPM upstream TBD). | -| portal.sankofa.nexus | web | https://portal.sankofa.nexus | Client SSO: portal / marketplace (typical upstream VMID 7801). | +| portal.sankofa.nexus | web | https://portal.sankofa.nexus | Client SSO: portal / marketplace (typical upstream VMID 7801). Add DNS + NPM row via `update-npmplus-proxy-hosts-api.sh`; NextAuth public URL `https://portal.sankofa.nexus`. | | dash.sankofa.nexus | web | https://dash.sankofa.nexus | Operator systems dashboard (IP allowlist + MFA intent; upstream TBD). | | docs.d-bis.org | web | https://docs.d-bis.org | Docs on explorer nginx where configured. | | blockscout.defi-oracle.io | web | https://blockscout.defi-oracle.io | Generic Blockscout hostname (often VMID 5000); not canonical Chain 138 **explorer.d-bis.org**. | diff --git a/docs/04-configuration/FQDN_EXPECTED_CONTENT.md b/docs/04-configuration/FQDN_EXPECTED_CONTENT.md index ce82f9c..23491b7 100644 --- a/docs/04-configuration/FQDN_EXPECTED_CONTENT.md +++ b/docs/04-configuration/FQDN_EXPECTED_CONTENT.md @@ -1,6 +1,6 @@ # FQDN expected content (what users and clients should see) -**Last Updated:** 2026-03-27 (aligned with EXPECTED_WEB_CONTENT deployment table v1.5) +**Last Updated:** 2026-03-29 (NPM fleet script includes `portal` / `admin` / optional `dash`; apex uses `IP_SANKOFA_PUBLIC_WEB`) **Purpose:** One-page description of **what should be presented** at each public NPM-routed hostname after HTTPS. Use this before pruning evidence or changing proxies so expectations stay aligned with product intent. **Canonical routing (IPs, VMIDs, ports):** [ALL_VMIDS_ENDPOINTS.md](ALL_VMIDS_ENDPOINTS.md), [RPC_ENDPOINTS_MASTER.md](RPC_ENDPOINTS_MASTER.md). @@ -30,7 +30,7 @@ | FQDN | Kind | What should be displayed or returned | |------|------|--------------------------------------| -| `sankofa.nexus` | Web | **Sankofa — Sovereign Technologies:** public corporate / brand web (mission, narrative, entry points). | +| `sankofa.nexus` | Web | **Sankofa — Sovereign Technologies:** public corporate / brand web (mission, narrative, entry points). NPM upstream: **`IP_SANKOFA_PUBLIC_WEB`:`SANKOFA_PUBLIC_WEB_PORT`** (defaults to portal IP until marketing CT is split). | | `www.sankofa.nexus` | 301 → apex | Browser ends on `https://sankofa.nexus/...`. | | `phoenix.sankofa.nexus` | Web / API | **Phoenix Cloud Services** (division of Sankofa): public-facing **division web** (intent). Same deployment may still expose API paths (`/health`, `/graphql`, …). E2E verifier may use `/health`. | | `www.phoenix.sankofa.nexus` | 301 → apex | Browser ends on `https://phoenix.sankofa.nexus/...`. | @@ -48,8 +48,8 @@ | FQDN | VMID / target | Notes | |------|---------------|--------| | `keycloak.sankofa.nexus` | **7802** (detail in [ALL_VMIDS_ENDPOINTS.md](ALL_VMIDS_ENDPOINTS.md)) | IdP + `/admin` for platform operators | -| `portal.sankofa.nexus` | **7801** · `192.168.11.51:3000` | ✅ **Active** when NPM routes here; public OIDC / `NEXTAUTH_URL` via `scripts/deployment/sync-sankofa-portal-7801.sh` | -| `admin.sankofa.nexus` | 🔶 **Not pinned** in VM inventory | Hostname **intent**; NPM + app upstream TBD; may share **7801** until split | +| `portal.sankofa.nexus` | **`IP_SANKOFA_CLIENT_SSO`** (typ. **7801** · `192.168.11.51:3000`) | Fleet script creates/updates NPM row; default **`NEXTAUTH_URL=https://portal.sankofa.nexus`** (`sync-sankofa-portal-7801.sh`) | +| `admin.sankofa.nexus` | same as **`IP_SANKOFA_CLIENT_SSO`** | Shares portal upstream until split; NPM row in fleet script | ### Operator / systems (IP-gated + MFA) @@ -121,7 +121,7 @@ ## Operator checklist -- **Wrong content** (e.g. explorer UI on `sankofa.nexus`, or HTML on RPC hostname) usually means **NPM upstream** or **DNS** is wrong — fix with `update-npmplus-proxy-hosts-api.sh` and [ALL_VMIDS_ENDPOINTS.md](ALL_VMIDS_ENDPOINTS.md). +- **Wrong content** (e.g. explorer UI on `sankofa.nexus`, or HTML on RPC hostname) usually means **NPM upstream** or **DNS** is wrong — fix with `update-npmplus-proxy-hosts-api.sh` and [ALL_VMIDS_ENDPOINTS.md](ALL_VMIDS_ENDPOINTS.md). Ensure **`portal.sankofa.nexus`** / **`admin.sankofa.nexus`** DNS exist; **`dash`** is created in NPM only when **`IP_SANKOFA_DASH`** is set in `config/ip-addresses.conf`. - **301 on `www.*`** is intentional; content is judged on the **apex** hostname after redirect. --- diff --git a/docs/MASTER_INDEX.md b/docs/MASTER_INDEX.md index 10fe1e8..642477a 100644 --- a/docs/MASTER_INDEX.md +++ b/docs/MASTER_INDEX.md @@ -48,6 +48,7 @@ | Contract / address status | [11-references/ADDRESS_MATRIX_AND_STATUS.md](11-references/ADDRESS_MATRIX_AND_STATUS.md), [11-references/CONTRACT_ADDRESSES_REFERENCE.md](11-references/CONTRACT_ADDRESSES_REFERENCE.md), [11-references/CONTRACT_NEXT_STEPS_LIST.md](11-references/CONTRACT_NEXT_STEPS_LIST.md) (59-addr check) | CONTRACT_INVENTORY_AND_VERIFICATION (deleted) | | **Proxmox VMIDs, LAN IPs, NPM targets** | [04-configuration/ALL_VMIDS_ENDPOINTS.md](04-configuration/ALL_VMIDS_ENDPOINTS.md), [`config/ip-addresses.conf`](../config/ip-addresses.conf), [11-references/NETWORK_CONFIGURATION_MASTER.md](11-references/NETWORK_CONFIGURATION_MASTER.md), [`config/proxmox-operational-template.json`](../config/proxmox-operational-template.json) | Dated inventories under `docs/archive/` (paths on disk only) | | **FQDN → expected content (web / API / RPC)** | [04-configuration/FQDN_EXPECTED_CONTENT.md](04-configuration/FQDN_EXPECTED_CONTENT.md) | — | +| **Sankofa / Phoenix public vs portal vs admin endpoints (fix list)** | [03-deployment/SANKOFA_PHOENIX_PUBLIC_PORTAL_ADMIN_ENDPOINT_CORRECTION_TASKS.md](03-deployment/SANKOFA_PHOENIX_PUBLIC_PORTAL_ADMIN_ENDPOINT_CORRECTION_TASKS.md) | — | | **IP conflict resolutions** | [reports/status/IP_CONFLICTS_RESOLUTION_COMPLETE.md](../reports/status/IP_CONFLICTS_RESOLUTION_COMPLETE.md), `scripts/resolve-ip-conflicts.sh` | — | --- @@ -58,7 +59,7 @@ |------|-----------------| | **00-meta** (tasks, next steps, phases) | [00-meta/NEXT_STEPS_INDEX.md](00-meta/NEXT_STEPS_INDEX.md), [00-meta/PHASES_AND_TASKS_MASTER.md](00-meta/PHASES_AND_TASKS_MASTER.md) | | **02-architecture** | [02-architecture/](02-architecture/) — **Public sector + Phoenix catalog baseline:** [02-architecture/PUBLIC_SECTOR_TENANCY_MARKETPLACE_AND_DEPLOYMENT_BASELINE.md](02-architecture/PUBLIC_SECTOR_TENANCY_MARKETPLACE_AND_DEPLOYMENT_BASELINE.md); **non-goals (incl. catalog vs marketing §9):** [02-architecture/NON_GOALS.md](02-architecture/NON_GOALS.md); **DBIS Chain 138:** [dbis_chain_138_technical_master_plan.md](../dbis_chain_138_technical_master_plan.md), [02-architecture/DBIS_NODE_ROLE_MATRIX.md](02-architecture/DBIS_NODE_ROLE_MATRIX.md), [02-architecture/DBIS_PHASE2_PROXMOX_SOVEREIGNIZATION_ROADMAP.md](02-architecture/DBIS_PHASE2_PROXMOX_SOVEREIGNIZATION_ROADMAP.md) | -| **03-deployment** | [03-deployment/OPERATIONAL_RUNBOOKS.md](03-deployment/OPERATIONAL_RUNBOOKS.md), [03-deployment/DEPLOYMENT_ORDER_OF_OPERATIONS.md](03-deployment/DEPLOYMENT_ORDER_OF_OPERATIONS.md), **Public sector live checklist:** [03-deployment/PUBLIC_SECTOR_LIVE_DEPLOYMENT_CHECKLIST.md](03-deployment/PUBLIC_SECTOR_LIVE_DEPLOYMENT_CHECKLIST.md), **Proxmox VE ops template:** [03-deployment/PROXMOX_VE_OPERATIONAL_DEPLOYMENT_TEMPLATE.md](03-deployment/PROXMOX_VE_OPERATIONAL_DEPLOYMENT_TEMPLATE.md) · [`config/proxmox-operational-template.json`](config/proxmox-operational-template.json); **DBIS Phase 1–3:** [03-deployment/PHASE1_DISCOVERY_RUNBOOK.md](03-deployment/PHASE1_DISCOVERY_RUNBOOK.md), [03-deployment/DBIS_PHASE3_E2E_PRODUCTION_SIMULATION_RUNBOOK.md](03-deployment/DBIS_PHASE3_E2E_PRODUCTION_SIMULATION_RUNBOOK.md), [03-deployment/CALIPER_CHAIN138_PERF_HOOK.md](03-deployment/CALIPER_CHAIN138_PERF_HOOK.md), [03-deployment/DBIS_HYPERLEDGER_RUNTIME_STATUS.md](03-deployment/DBIS_HYPERLEDGER_RUNTIME_STATUS.md), [03-deployment/DBIS_PHASES_1_TO_3_PRODUCTION_GATE.md](03-deployment/DBIS_PHASES_1_TO_3_PRODUCTION_GATE.md), **RTGS canonical production checklist and institutional-finance layers:** [03-deployment/DBIS_RTGS_E2E_REQUIREMENTS_MATRIX.md](03-deployment/DBIS_RTGS_E2E_REQUIREMENTS_MATRIX.md), [03-deployment/DBIS_RTGS_FX_TRANSACTION_CATALOG.md](03-deployment/DBIS_RTGS_FX_TRANSACTION_CATALOG.md), [03-deployment/DBIS_RTGS_DEPOSITORY_AND_CUSTODY_OPERATING_MODEL.md](03-deployment/DBIS_RTGS_DEPOSITORY_AND_CUSTODY_OPERATING_MODEL.md), [03-deployment/DBIS_RTGS_FX_AND_LIQUIDITY_OPERATING_MODEL.md](03-deployment/DBIS_RTGS_FX_AND_LIQUIDITY_OPERATING_MODEL.md), [03-deployment/DBIS_RTGS_CONTROL_PLANE_DEPLOYMENT_CHECKLIST.md](03-deployment/DBIS_RTGS_CONTROL_PLANE_DEPLOYMENT_CHECKLIST.md), [03-deployment/DBIS_RTGS_LATER_PHASE_SIDECARS_DEPLOYMENT_CHECKLIST.md](03-deployment/DBIS_RTGS_LATER_PHASE_SIDECARS_DEPLOYMENT_CHECKLIST.md), [03-deployment/DBIS_OMNL_INDONESIA_BNI_E2E_INTEGRATION_BLUEPRINT.md](03-deployment/DBIS_OMNL_INDONESIA_BNI_E2E_INTEGRATION_BLUEPRINT.md), [03-deployment/DBIS_RTGS_FIRST_SLICE_ARCHITECTURE.md](03-deployment/DBIS_RTGS_FIRST_SLICE_ARCHITECTURE.md), [03-deployment/DBIS_RTGS_FIRST_SLICE_DEPLOYMENT_CHECKLIST.md](03-deployment/DBIS_RTGS_FIRST_SLICE_DEPLOYMENT_CHECKLIST.md), [03-deployment/DBIS_HYBX_SIDECAR_BOUNDARY_MATRIX.md](03-deployment/DBIS_HYBX_SIDECAR_BOUNDARY_MATRIX.md), [03-deployment/DBIS_MOJALOOP_INTEGRATION_STATUS.md](03-deployment/DBIS_MOJALOOP_INTEGRATION_STATUS.md), [03-deployment/DBIS_HYPERLEDGER_IDENTITY_STACK_DECISION.md](03-deployment/DBIS_HYPERLEDGER_IDENTITY_STACK_DECISION.md) | +| **03-deployment** | [03-deployment/OPERATIONAL_RUNBOOKS.md](03-deployment/OPERATIONAL_RUNBOOKS.md), [03-deployment/DEPLOYMENT_ORDER_OF_OPERATIONS.md](03-deployment/DEPLOYMENT_ORDER_OF_OPERATIONS.md), **Public sector live checklist:** [03-deployment/PUBLIC_SECTOR_LIVE_DEPLOYMENT_CHECKLIST.md](03-deployment/PUBLIC_SECTOR_LIVE_DEPLOYMENT_CHECKLIST.md), **Proxmox VE ops template:** [03-deployment/PROXMOX_VE_OPERATIONAL_DEPLOYMENT_TEMPLATE.md](03-deployment/PROXMOX_VE_OPERATIONAL_DEPLOYMENT_TEMPLATE.md) · [`config/proxmox-operational-template.json`](config/proxmox-operational-template.json); **DBIS Phase 1–3:** [03-deployment/PHASE1_DISCOVERY_RUNBOOK.md](03-deployment/PHASE1_DISCOVERY_RUNBOOK.md), [03-deployment/DBIS_PHASE3_E2E_PRODUCTION_SIMULATION_RUNBOOK.md](03-deployment/DBIS_PHASE3_E2E_PRODUCTION_SIMULATION_RUNBOOK.md), [03-deployment/CALIPER_CHAIN138_PERF_HOOK.md](03-deployment/CALIPER_CHAIN138_PERF_HOOK.md), [03-deployment/DBIS_HYPERLEDGER_RUNTIME_STATUS.md](03-deployment/DBIS_HYPERLEDGER_RUNTIME_STATUS.md), [03-deployment/DBIS_PHASES_1_TO_3_PRODUCTION_GATE.md](03-deployment/DBIS_PHASES_1_TO_3_PRODUCTION_GATE.md), **RTGS canonical production checklist and institutional-finance layers:** [03-deployment/DBIS_RTGS_E2E_REQUIREMENTS_MATRIX.md](03-deployment/DBIS_RTGS_E2E_REQUIREMENTS_MATRIX.md), [03-deployment/DBIS_RTGS_FX_TRANSACTION_CATALOG.md](03-deployment/DBIS_RTGS_FX_TRANSACTION_CATALOG.md), [03-deployment/DBIS_RTGS_DEPOSITORY_AND_CUSTODY_OPERATING_MODEL.md](03-deployment/DBIS_RTGS_DEPOSITORY_AND_CUSTODY_OPERATING_MODEL.md), [03-deployment/DBIS_RTGS_FX_AND_LIQUIDITY_OPERATING_MODEL.md](03-deployment/DBIS_RTGS_FX_AND_LIQUIDITY_OPERATING_MODEL.md), [03-deployment/DBIS_RTGS_CONTROL_PLANE_DEPLOYMENT_CHECKLIST.md](03-deployment/DBIS_RTGS_CONTROL_PLANE_DEPLOYMENT_CHECKLIST.md), [03-deployment/DBIS_RTGS_LATER_PHASE_SIDECARS_DEPLOYMENT_CHECKLIST.md](03-deployment/DBIS_RTGS_LATER_PHASE_SIDECARS_DEPLOYMENT_CHECKLIST.md), [03-deployment/DBIS_OMNL_INDONESIA_BNI_E2E_INTEGRATION_BLUEPRINT.md](03-deployment/DBIS_OMNL_INDONESIA_BNI_E2E_INTEGRATION_BLUEPRINT.md), [03-deployment/DBIS_OMNL_INDONESIA_BNI_E2E_EXECUTABLE_TASK_LIST.md](03-deployment/DBIS_OMNL_INDONESIA_BNI_E2E_EXECUTABLE_TASK_LIST.md), [03-deployment/DBIS_OMNL_INDONESIA_BNI_E2E_EXECUTION_STATUS_2026-03-29.md](03-deployment/DBIS_OMNL_INDONESIA_BNI_E2E_EXECUTION_STATUS_2026-03-29.md), [03-deployment/DBIS_RTGS_FIRST_SLICE_ARCHITECTURE.md](03-deployment/DBIS_RTGS_FIRST_SLICE_ARCHITECTURE.md), [03-deployment/DBIS_RTGS_FIRST_SLICE_DEPLOYMENT_CHECKLIST.md](03-deployment/DBIS_RTGS_FIRST_SLICE_DEPLOYMENT_CHECKLIST.md), [03-deployment/DBIS_HYBX_SIDECAR_BOUNDARY_MATRIX.md](03-deployment/DBIS_HYBX_SIDECAR_BOUNDARY_MATRIX.md), [03-deployment/DBIS_MOJALOOP_INTEGRATION_STATUS.md](03-deployment/DBIS_MOJALOOP_INTEGRATION_STATUS.md), [03-deployment/DBIS_HYPERLEDGER_IDENTITY_STACK_DECISION.md](03-deployment/DBIS_HYPERLEDGER_IDENTITY_STACK_DECISION.md), [03-deployment/DBIS_IDENTITY_COMPLETION_PACKAGE_RUNBOOK.md](03-deployment/DBIS_IDENTITY_COMPLETION_PACKAGE_RUNBOOK.md) | | **04-configuration** | [04-configuration/README.md](04-configuration/README.md), [04-configuration/ADDITIONAL_PATHS_AND_EXTENSIONS.md](04-configuration/ADDITIONAL_PATHS_AND_EXTENSIONS.md) (paths, registry, token-mapping, LiFi/Jumper); **Chain 138 wallets:** [04-configuration/CHAIN138_WALLET_CONFIG_VALIDATION.md](04-configuration/CHAIN138_WALLET_CONFIG_VALIDATION.md); **Chain 2138 testnet wallets:** [04-configuration/CHAIN2138_WALLET_CONFIG_VALIDATION.md](04-configuration/CHAIN2138_WALLET_CONFIG_VALIDATION.md); **OMNL Indonesia / HYBX-BATCH-001:** [04-configuration/mifos-omnl-central-bank/HYBX_BATCH_001_OPERATOR_CHECKLIST.md](04-configuration/mifos-omnl-central-bank/HYBX_BATCH_001_OPERATOR_CHECKLIST.md), [04-configuration/mifos-omnl-central-bank/INDONESIA_PACKAGE_4_995_EVIDENCE_STANDARD.md](04-configuration/mifos-omnl-central-bank/INDONESIA_PACKAGE_4_995_EVIDENCE_STANDARD.md) | | **06-besu** | [06-besu/MASTER_INDEX.md](06-besu/MASTER_INDEX.md) | | **Testnet (2138)** | [testnet/DEFI_ORACLE_META_TESTNET_2138_RUNBOOK.md](testnet/DEFI_ORACLE_META_TESTNET_2138_RUNBOOK.md), [testnet/TESTNET_DEPLOYMENT.md](testnet/TESTNET_DEPLOYMENT.md) | diff --git a/scripts/deployment/enable-sankofa-portal-login-7801.sh b/scripts/deployment/enable-sankofa-portal-login-7801.sh index 518c480..18453ac 100755 --- a/scripts/deployment/enable-sankofa-portal-login-7801.sh +++ b/scripts/deployment/enable-sankofa-portal-login-7801.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Enable working login on https://sankofa.nexus: +# Enable working login on the client portal hostname (https://portal.sankofa.nexus; NEXTAUTH_URL): # - Fix Keycloak systemd (JAVA_HOME line; hostname + proxy headers for NPM). # - Remove .env.local on CT 7801; install .env with PORTAL_LOCAL_LOGIN_* + NEXTAUTH_SECRET. # - Run sync-sankofa-portal-7801.sh (rebuild portal with updated auth.ts). @@ -32,7 +32,7 @@ cat > "$ENV_TMP" </dev/null || true + set +a + set -u +fi + +KEYCLOAK_URL="${KEYCLOAK_URL:-https://keycloak.sankofa.nexus}" +REALM="${KEYCLOAK_REALM:-master}" +CLIENT_ID="${KEYCLOAK_CLIENT_ID:-sankofa-portal}" +ADMIN_USER="${KEYCLOAK_ADMIN:-admin}" +ADMIN_PASS="${KEYCLOAK_ADMIN_PASSWORD:-}" + +DRY=0 +[[ "${1:-}" == "--dry-run" ]] && DRY=1 + +if [ -z "$ADMIN_PASS" ]; then + echo "KEYCLOAK_ADMIN_PASSWORD is not set. Add it to .env (or export) and re-run." >&2 + echo "Without it, update the client manually: Valid redirect URIs + Web origins for portal/admin." >&2 + exit 1 +fi + +TOKEN_JSON=$(curl -sS -X POST "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \ + -d "grant_type=password" \ + -d "client_id=admin-cli" \ + -d "username=${ADMIN_USER}" \ + -d "password=${ADMIN_PASS}") + +TOKEN=$(echo "$TOKEN_JSON" | jq -r '.access_token // empty') +if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then + echo "Failed to obtain admin token (check URL, user, password)." >&2 + echo "$TOKEN_JSON" | jq -r '.error_description // .error // .' >&2 + exit 1 +fi + +CLIENT_LIST=$(curl -sS -G "${KEYCLOAK_URL}/admin/realms/${REALM}/clients" \ + -H "Authorization: Bearer ${TOKEN}" \ + --data-urlencode "clientId=${CLIENT_ID}") + +INTERNAL_ID=$(echo "$CLIENT_LIST" | jq -r '.[0].id // empty') +if [ -z "$INTERNAL_ID" ] || [ "$INTERNAL_ID" = "null" ]; then + echo "No client with clientId=${CLIENT_ID} in realm ${REALM}." >&2 + exit 1 +fi + +FULL=$(curl -sS "${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${INTERNAL_ID}" \ + -H "Authorization: Bearer ${TOKEN}") + +DESIRED_REDIRECTS='[ + "https://portal.sankofa.nexus/*", + "https://portal.sankofa.nexus", + "https://admin.sankofa.nexus/*", + "https://admin.sankofa.nexus" +]' +DESIRED_ORIGINS='[ + "https://portal.sankofa.nexus", + "https://admin.sankofa.nexus" +]' + +MERGED=$(echo "$FULL" | jq --argjson dr "$DESIRED_REDIRECTS" --argjson wo "$DESIRED_ORIGINS" ' + .redirectUris = ((.redirectUris // []) + $dr | unique) | + .webOrigins = ((.webOrigins // []) + $wo | unique) +') + +if [ "$DRY" = 1 ]; then + echo "[dry-run] Would set client ${CLIENT_ID} (${INTERNAL_ID}) to:" + echo "$MERGED" | jq '{clientId, redirectUris, webOrigins}' + exit 0 +fi + +HTTP_CODE=$(curl -sS -o /tmp/kc_put_body.txt -w "%{http_code}" -X PUT \ + "${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${INTERNAL_ID}" \ + -H "Authorization: Bearer ${TOKEN}" \ + -H "Content-Type: application/json" \ + -d "$MERGED") + +if [[ "$HTTP_CODE" != "204" && "$HTTP_CODE" != "200" ]]; then + echo "PUT client failed HTTP ${HTTP_CODE}" >&2 + cat /tmp/kc_put_body.txt >&2 || true + exit 1 +fi + +rm -f /tmp/kc_put_body.txt +echo "Updated Keycloak client ${CLIENT_ID}: redirect URIs and web origins merged (portal + admin)." diff --git a/scripts/deployment/provision-sankofa-public-web-lxc-7806.sh b/scripts/deployment/provision-sankofa-public-web-lxc-7806.sh new file mode 100755 index 0000000..f9f2b36 --- /dev/null +++ b/scripts/deployment/provision-sankofa-public-web-lxc-7806.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# Create LXC 7806 (sankofa-public-web) for corporate Next.js at repo root → sankofa.nexus via IP_SANKOFA_PUBLIC_WEB. +# Installs Node 20 + pnpm and systemd unit; then run sync-sankofa-public-web-to-ct.sh and NPM fleet update. +# +# Usage (from repo root, SSH to r630-01): +# bash scripts/deployment/provision-sankofa-public-web-lxc-7806.sh [--dry-run] +# +# Env: PROXMOX_HOST, SANKOFA_PUBLIC_WEB_VMID (7806), IP_SANKOFA_PUBLIC_WEB_CT (default 192.168.11.63) +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +# shellcheck source=/dev/null +source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true + +PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}" +VMID="${SANKOFA_PUBLIC_WEB_VMID:-7806}" +IP_CT="${IP_SANKOFA_PUBLIC_WEB_CT:-192.168.11.63}" +HOSTNAME_CT="${SANKOFA_PUBLIC_WEB_HOSTNAME:-sankofa-public-web}" +TEMPLATE="${TEMPLATE:-local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst}" +STORAGE="${STORAGE:-local-lvm}" +NETWORK="${NETWORK:-vmbr0}" +GATEWAY="${NETWORK_GATEWAY:-192.168.11.1}" +SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new" +SERVICE_FILE="${PROJECT_ROOT}/config/systemd/sankofa-public-web.service" + +DRY_RUN=false +[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true + +echo "=== Provision Sankofa public web LXC ${VMID} (${IP_CT}) ===" +echo "Proxmox: ${PROXMOX_HOST}" + +if [[ ! -f "$SERVICE_FILE" ]]; then + echo "ERROR: Missing $SERVICE_FILE" + exit 1 +fi + +if $DRY_RUN; then + echo "[DRY-RUN] Would create CT ${VMID} if missing, install Node/pnpm, install systemd unit" + exit 0 +fi + +if ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct list 2>/dev/null | grep -q '^${VMID} '"; then + echo "CT ${VMID} already exists — skipping pct create" +else + echo "Creating CT ${VMID} (${HOSTNAME_CT}) @ ${IP_CT}/24..." + ssh $SSH_OPTS "root@${PROXMOX_HOST}" bash -s </dev/null 2>&1 || ! node -v | grep -q \"^v20\"; then + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y -qq nodejs +fi +command -v pnpm >/dev/null 2>&1 || npm install -g pnpm +mkdir -p /opt/sankofa-public-web +'" + +echo "Installing systemd unit..." +scp $SSH_OPTS "$SERVICE_FILE" "root@${PROXMOX_HOST}:/tmp/sankofa-public-web.service" +ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct push ${VMID} /tmp/sankofa-public-web.service /etc/systemd/system/sankofa-public-web.service && rm -f /tmp/sankofa-public-web.service" +ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- systemctl daemon-reload" +ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- systemctl enable sankofa-public-web" + +echo "✅ CT ${VMID} ready. Next:" +echo " SANKOFA_PUBLIC_WEB_VMID=${VMID} bash scripts/deployment/sync-sankofa-public-web-to-ct.sh" +echo " Then set IP_SANKOFA_PUBLIC_WEB=${IP_CT} and run scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh" diff --git a/scripts/deployment/sankofa-portal-ensure-nextauth-on-ct.sh b/scripts/deployment/sankofa-portal-ensure-nextauth-on-ct.sh index 25d098c..d0c9a2d 100755 --- a/scripts/deployment/sankofa-portal-ensure-nextauth-on-ct.sh +++ b/scripts/deployment/sankofa-portal-ensure-nextauth-on-ct.sh @@ -14,7 +14,7 @@ PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}" VMID="${SANKOFA_PORTAL_VMID:-7801}" CT_APP_DIR="${SANKOFA_PORTAL_CT_DIR:-/opt/sankofa-portal}" SERVICE_NAME="${SANKOFA_PORTAL_SERVICE:-sankofa-portal}" -NEXTAUTH_PUBLIC_URL="${SANKOFA_PORTAL_NEXTAUTH_URL:-https://sankofa.nexus}" +NEXTAUTH_PUBLIC_URL="${SANKOFA_PORTAL_NEXTAUTH_URL:-https://portal.sankofa.nexus}" SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new" ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- bash -s" </dev/null || true + +PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}" +VMID="${SANKOFA_PUBLIC_WEB_VMID:-7806}" +CT_APP_DIR="${SANKOFA_PUBLIC_WEB_CT_DIR:-/opt/sankofa-public-web}" +SERVICE_NAME="${SANKOFA_PUBLIC_WEB_SERVICE:-sankofa-public-web}" +SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new" + +DEFAULT_SRC="${PROJECT_ROOT}/../Sankofa" +if [[ -d "$DEFAULT_SRC" && -f "$DEFAULT_SRC/package.json" ]]; then + SANKOFA_PUBLIC_WEB_SRC="${SANKOFA_PUBLIC_WEB_SRC:-$DEFAULT_SRC}" +else + SANKOFA_PUBLIC_WEB_SRC="${SANKOFA_PUBLIC_WEB_SRC:-}" +fi + +DRY_RUN=false +[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true + +echo "=== Sync Sankofa public (repo root) → CT ${VMID} (${CT_APP_DIR}) ===" +echo "Proxmox: ${PROXMOX_HOST}" +echo "Source: ${SANKOFA_PUBLIC_WEB_SRC:-}" +echo "" + +if [[ -z "$SANKOFA_PUBLIC_WEB_SRC" || ! -d "$SANKOFA_PUBLIC_WEB_SRC" ]]; then + echo "ERROR: Set SANKOFA_PUBLIC_WEB_SRC to the Sankofa monorepo root (parent of portal/)." + echo "Example: SANKOFA_PUBLIC_WEB_SRC=/path/to/Sankofa $0" + exit 1 +fi + +if ! command -v tar >/dev/null; then + echo "ERROR: tar required" + exit 1 +fi + +TMP_TGZ="${TMPDIR:-/tmp}/sankofa-public-web-sync-$$.tgz" +REMOTE_TGZ="/tmp/sankofa-public-web-sync-$$.tgz" +CT_TGZ="/tmp/sankofa-public-web-sync.tgz" + +cleanup() { rm -f "$TMP_TGZ"; } +trap cleanup EXIT + +if $DRY_RUN; then + echo "[DRY-RUN] tar (exclude node_modules,.next,.git) → $TMP_TGZ" + echo "[DRY-RUN] scp → root@${PROXMOX_HOST}:${REMOTE_TGZ}" + echo "[DRY-RUN] pct push ${VMID} … && systemctl stop ${SERVICE_NAME}" + echo "[DRY-RUN] pnpm install && pnpm build && systemctl start ${SERVICE_NAME}" + exit 0 +fi + +echo "📦 Archiving Sankofa repo root (excluding node_modules, .next, .git, .env / .env.local)…" +tar czf "$TMP_TGZ" \ + --exclude=node_modules \ + --exclude=.next \ + --exclude=portal/node_modules \ + --exclude=portal/.next \ + --exclude=.git \ + --exclude=.env.local \ + --exclude=.env \ + -C "$SANKOFA_PUBLIC_WEB_SRC" . + +echo "📤 Copy to Proxmox host…" +scp $SSH_OPTS "$TMP_TGZ" "root@${PROXMOX_HOST}:${REMOTE_TGZ}" + +echo "📥 Push into CT ${VMID} and build…" +ssh $SSH_OPTS "root@${PROXMOX_HOST}" bash -s </dev/null || { echo "ERROR: pnpm missing in CT"; exit 1; } + pnpm install + pnpm build +' +pct exec ${VMID} -- systemctl start ${SERVICE_NAME} +pct exec ${VMID} -- systemctl is-active ${SERVICE_NAME} +REMOTE_EOF + +echo "" +echo "✅ Done. Point NPM apex with IP_SANKOFA_PUBLIC_WEB / SANKOFA_PUBLIC_WEB_PORT, then:" +echo " bash scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh" +echo " curl -sS http://${IP_SANKOFA_PUBLIC_WEB:-}:${SANKOFA_PUBLIC_WEB_PORT:-3000}/ | head -c 120" diff --git a/scripts/nginx-proxy-manager/migrate-to-npmplus.sh b/scripts/nginx-proxy-manager/migrate-to-npmplus.sh index f363ef6..66c3ad9 100755 --- a/scripts/nginx-proxy-manager/migrate-to-npmplus.sh +++ b/scripts/nginx-proxy-manager/migrate-to-npmplus.sh @@ -284,24 +284,34 @@ create_proxy_host() { return 0 } -# Configure all 19 domains -echo "🚀 Starting domain configuration (19 domains)..." +# Configure core domains (count varies; sankofa zone includes portal/admin/dash when dash IP set) +echo "🚀 Starting domain configuration..." echo "" SUCCESS=0 FAILED=0 -# sankofa.nexus (5 domains) — portal :3000 / Phoenix API :4000 (not Blockscout; explorer is IP_BLOCKSCOUT:80) +# sankofa.nexus zone — corporate apex (IP_SANKOFA_PUBLIC_WEB), Phoenix API :4000, client SSO (portal/admin), optional dash (IP_SANKOFA_DASH). Not Blockscout. IP_SANKOFA_PORTAL="${IP_SANKOFA_PORTAL:-${IP_SERVICE_51:-192.168.11.51}}" IP_SANKOFA_PHOENIX_API="${IP_SANKOFA_PHOENIX_API:-${IP_SERVICE_50:-192.168.11.50}}" SANKOFA_PORTAL_PORT="${SANKOFA_PORTAL_PORT:-3000}" SANKOFA_PHOENIX_API_PORT="${SANKOFA_PHOENIX_API_PORT:-4000}" +IP_SANKOFA_PUBLIC_WEB="${IP_SANKOFA_PUBLIC_WEB:-${IP_SANKOFA_PORTAL}}" +SANKOFA_PUBLIC_WEB_PORT="${SANKOFA_PUBLIC_WEB_PORT:-${SANKOFA_PORTAL_PORT}}" +IP_SANKOFA_CLIENT_SSO="${IP_SANKOFA_CLIENT_SSO:-${IP_SANKOFA_PORTAL}}" +SANKOFA_CLIENT_SSO_PORT="${SANKOFA_CLIENT_SSO_PORT:-${SANKOFA_PORTAL_PORT}}" IP_ORDER_HAPROXY="${IP_ORDER_HAPROXY:-192.168.11.39}" -create_proxy_host "sankofa.nexus" "http" "${IP_SANKOFA_PORTAL}" "${SANKOFA_PORTAL_PORT}" "false" && ((SUCCESS++)) || ((FAILED++)) -create_proxy_host "www.sankofa.nexus" "http" "${IP_SANKOFA_PORTAL}" "${SANKOFA_PORTAL_PORT}" "false" && ((SUCCESS++)) || ((FAILED++)) +create_proxy_host "sankofa.nexus" "http" "${IP_SANKOFA_PUBLIC_WEB}" "${SANKOFA_PUBLIC_WEB_PORT}" "false" && ((SUCCESS++)) || ((FAILED++)) +create_proxy_host "www.sankofa.nexus" "http" "${IP_SANKOFA_PUBLIC_WEB}" "${SANKOFA_PUBLIC_WEB_PORT}" "false" && ((SUCCESS++)) || ((FAILED++)) create_proxy_host "phoenix.sankofa.nexus" "http" "${IP_SANKOFA_PHOENIX_API}" "${SANKOFA_PHOENIX_API_PORT}" "false" && ((SUCCESS++)) || ((FAILED++)) create_proxy_host "www.phoenix.sankofa.nexus" "http" "${IP_SANKOFA_PHOENIX_API}" "${SANKOFA_PHOENIX_API_PORT}" "false" && ((SUCCESS++)) || ((FAILED++)) create_proxy_host "the-order.sankofa.nexus" "http" "${IP_ORDER_HAPROXY}" "80" "false" && ((SUCCESS++)) || ((FAILED++)) +create_proxy_host "portal.sankofa.nexus" "http" "${IP_SANKOFA_CLIENT_SSO}" "${SANKOFA_CLIENT_SSO_PORT}" "false" && ((SUCCESS++)) || ((FAILED++)) +create_proxy_host "admin.sankofa.nexus" "http" "${IP_SANKOFA_CLIENT_SSO}" "${SANKOFA_CLIENT_SSO_PORT}" "false" && ((SUCCESS++)) || ((FAILED++)) +if [[ -n "${IP_SANKOFA_DASH:-}" ]]; then + SANKOFA_DASH_PORT="${SANKOFA_DASH_PORT:-3000}" + create_proxy_host "dash.sankofa.nexus" "http" "${IP_SANKOFA_DASH}" "${SANKOFA_DASH_PORT}" "false" && ((SUCCESS++)) || ((FAILED++)) +fi # d-bis.org (9 domains) create_proxy_host "explorer.d-bis.org" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++)) diff --git a/scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh b/scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh index d9486c6..3993f25 100755 --- a/scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh +++ b/scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh @@ -373,15 +373,19 @@ update_proxy_host "dbis.xom-dev.phoenix.sankofa.nexus" "http://${IP_GOV_PORTALS_ update_proxy_host "iccc.xom-dev.phoenix.sankofa.nexus" "http://${IP_GOV_PORTALS_DEV}:3002" false && updated_count=$((updated_count + 1)) || { add_proxy_host "iccc.xom-dev.phoenix.sankofa.nexus" "${IP_GOV_PORTALS_DEV}" 3002 false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) update_proxy_host "omnl.xom-dev.phoenix.sankofa.nexus" "http://${IP_GOV_PORTALS_DEV}:3003" false && updated_count=$((updated_count + 1)) || { add_proxy_host "omnl.xom-dev.phoenix.sankofa.nexus" "${IP_GOV_PORTALS_DEV}" 3003 false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) update_proxy_host "xom.xom-dev.phoenix.sankofa.nexus" "http://${IP_GOV_PORTALS_DEV}:3004" false && updated_count=$((updated_count + 1)) || { add_proxy_host "xom.xom-dev.phoenix.sankofa.nexus" "${IP_GOV_PORTALS_DEV}" 3004 false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) -# Sankofa portal (Next.js CT 7801) and Phoenix API (Fastify CT 7800) — not Blockscout / SolaceScanScout (that is explorer.d-bis.org / IP_BLOCKSCOUT:80) -# Public web intent: sankofa.nexus = Sankofa Sovereign Technologies; phoenix.sankofa.nexus = Phoenix Cloud Services (division). Client SSO: admin / portal + keycloak IdP. Operator: dash (IP+MFA). See docs/02-architecture/EXPECTED_WEB_CONTENT.md. +# Sankofa / Phoenix — not Blockscout (explorer.d-bis.org / IP_BLOCKSCOUT:80). +# Intent: sankofa.nexus = corporate marketing (IP_SANKOFA_PUBLIC_WEB); portal.sankofa.nexus + admin = client SSO (IP_SANKOFA_CLIENT_SSO); phoenix = division + API. dash = operator (set IP_SANKOFA_DASH to manage). See docs/02-architecture/EXPECTED_WEB_CONTENT.md. # www.sankofa.nexus → 301 https://sankofa.nexus$request_uri; www.phoenix → phoenix; www.the-order → the-order (NPM advanced_config). IP_SANKOFA_PORTAL="${IP_SANKOFA_PORTAL:-${IP_SERVICE_51:-192.168.11.51}}" IP_SANKOFA_PHOENIX_API="${IP_SANKOFA_PHOENIX_API:-${IP_SERVICE_50:-192.168.11.50}}" SANKOFA_PORTAL_PORT="${SANKOFA_PORTAL_PORT:-3000}" SANKOFA_PHOENIX_API_PORT="${SANKOFA_PHOENIX_API_PORT:-4000}" -update_proxy_host "sankofa.nexus" "http://${IP_SANKOFA_PORTAL}:${SANKOFA_PORTAL_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "sankofa.nexus" "${IP_SANKOFA_PORTAL}" "${SANKOFA_PORTAL_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) -update_proxy_host "www.sankofa.nexus" "http://${IP_SANKOFA_PORTAL}:${SANKOFA_PORTAL_PORT}" false false "https://sankofa.nexus" && updated_count=$((updated_count + 1)) || { add_proxy_host "www.sankofa.nexus" "${IP_SANKOFA_PORTAL}" "${SANKOFA_PORTAL_PORT}" false false "https://sankofa.nexus" && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) +IP_SANKOFA_PUBLIC_WEB="${IP_SANKOFA_PUBLIC_WEB:-${IP_SANKOFA_PORTAL}}" +SANKOFA_PUBLIC_WEB_PORT="${SANKOFA_PUBLIC_WEB_PORT:-${SANKOFA_PORTAL_PORT}}" +IP_SANKOFA_CLIENT_SSO="${IP_SANKOFA_CLIENT_SSO:-${IP_SANKOFA_PORTAL}}" +SANKOFA_CLIENT_SSO_PORT="${SANKOFA_CLIENT_SSO_PORT:-${SANKOFA_PORTAL_PORT}}" +update_proxy_host "sankofa.nexus" "http://${IP_SANKOFA_PUBLIC_WEB}:${SANKOFA_PUBLIC_WEB_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "sankofa.nexus" "${IP_SANKOFA_PUBLIC_WEB}" "${SANKOFA_PUBLIC_WEB_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) +update_proxy_host "www.sankofa.nexus" "http://${IP_SANKOFA_PUBLIC_WEB}:${SANKOFA_PUBLIC_WEB_PORT}" false false "https://sankofa.nexus" && updated_count=$((updated_count + 1)) || { add_proxy_host "www.sankofa.nexus" "${IP_SANKOFA_PUBLIC_WEB}" "${SANKOFA_PUBLIC_WEB_PORT}" false false "https://sankofa.nexus" && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) update_proxy_host "phoenix.sankofa.nexus" "http://${IP_SANKOFA_PHOENIX_API}:${SANKOFA_PHOENIX_API_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "phoenix.sankofa.nexus" "${IP_SANKOFA_PHOENIX_API}" "${SANKOFA_PHOENIX_API_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) update_proxy_host "www.phoenix.sankofa.nexus" "http://${IP_SANKOFA_PHOENIX_API}:${SANKOFA_PHOENIX_API_PORT}" false false "https://phoenix.sankofa.nexus" && updated_count=$((updated_count + 1)) || { add_proxy_host "www.phoenix.sankofa.nexus" "${IP_SANKOFA_PHOENIX_API}" "${SANKOFA_PHOENIX_API_PORT}" false false "https://phoenix.sankofa.nexus" && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) # Keycloak (CT 7802) — portal SSO; NPM must forward X-Forwarded-* (Keycloak KC_PROXY_HEADERS=xforwarded on upstream) @@ -403,6 +407,16 @@ IP_SANKOFA_STUDIO="${IP_SANKOFA_STUDIO:-192.168.11.72}" SANKOFA_STUDIO_PORT="${SANKOFA_STUDIO_PORT:-8000}" # block_exploits false — studio UI/API may POST; align with portal policy (avoid spurious 405 from NPM WAF) update_proxy_host "studio.sankofa.nexus" "http://${IP_SANKOFA_STUDIO}:${SANKOFA_STUDIO_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "studio.sankofa.nexus" "${IP_SANKOFA_STUDIO}" "${SANKOFA_STUDIO_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) +# Client SSO hostnames (Next.js portal stack on 7801 typical). NEXTAUTH_URL / Keycloak redirects: https://portal.sankofa.nexus (and https://admin.sankofa.nexus). +update_proxy_host "portal.sankofa.nexus" "http://${IP_SANKOFA_CLIENT_SSO}:${SANKOFA_CLIENT_SSO_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "portal.sankofa.nexus" "${IP_SANKOFA_CLIENT_SSO}" "${SANKOFA_CLIENT_SSO_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) +update_proxy_host "admin.sankofa.nexus" "http://${IP_SANKOFA_CLIENT_SSO}:${SANKOFA_CLIENT_SSO_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "admin.sankofa.nexus" "${IP_SANKOFA_CLIENT_SSO}" "${SANKOFA_CLIENT_SSO_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) +# Operator systems dashboard — only when IP_SANKOFA_DASH is set (see config/ip-addresses.conf). +if [[ -n "${IP_SANKOFA_DASH:-}" ]]; then + SANKOFA_DASH_PORT="${SANKOFA_DASH_PORT:-3000}" + update_proxy_host "dash.sankofa.nexus" "http://${IP_SANKOFA_DASH}:${SANKOFA_DASH_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "dash.sankofa.nexus" "${IP_SANKOFA_DASH}" "${SANKOFA_DASH_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1)) +else + echo "ℹ️ Skipping dash.sankofa.nexus (set IP_SANKOFA_DASH and SANKOFA_DASH_PORT to provision upstream)." +fi echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/scripts/update-all-dns-to-public-ip.sh b/scripts/update-all-dns-to-public-ip.sh index 780b54f..6a23b34 100755 --- a/scripts/update-all-dns-to-public-ip.sh +++ b/scripts/update-all-dns-to-public-ip.sh @@ -330,6 +330,10 @@ main() { SANKOFA_RECORDS=( "@" # sankofa.nexus "www" # www.sankofa.nexus + "portal" # portal.sankofa.nexus (client SSO) + "admin" # admin.sankofa.nexus (client access admin) + "keycloak" # keycloak.sankofa.nexus (IdP) + "studio" # studio.sankofa.nexus (FusionAI Creator) "phoenix" # phoenix.sankofa.nexus "www.phoenix" # www.phoenix.sankofa.nexus "the-order" # the-order.sankofa.nexus diff --git a/scripts/update-sankofa-npmplus-proxy-hosts.sh b/scripts/update-sankofa-npmplus-proxy-hosts.sh index ff035cb..92bc4a7 100755 --- a/scripts/update-sankofa-npmplus-proxy-hosts.sh +++ b/scripts/update-sankofa-npmplus-proxy-hosts.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash set -euo pipefail -# Update Sankofa NPMplus proxy hosts (portal + Phoenix API + the-order) via API by numeric host ID. -# Prefer for production: scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh (domain-based, full fleet). +# Update Sankofa NPMplus proxy hosts (apex + Phoenix API + the-order) via API by numeric host ID. +# Prefer for production: scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh (domain-based; includes portal.sankofa.nexus, admin.sankofa.nexus, dash when IP_SANKOFA_DASH set). # NPM proxy host IDs below match backup backup-20260325_183932 (3–6, 7, 59); override with SANKOFA_NPM_ID_* if your DB differs. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -27,6 +27,8 @@ IP_SANKOFA_PORTAL="${IP_SANKOFA_PORTAL:-${IP_SERVICE_51:-192.168.11.51}}" IP_SANKOFA_PHOENIX_API="${IP_SANKOFA_PHOENIX_API:-${IP_SERVICE_50:-192.168.11.50}}" SANKOFA_PORTAL_PORT="${SANKOFA_PORTAL_PORT:-3000}" SANKOFA_PHOENIX_API_PORT="${SANKOFA_PHOENIX_API_PORT:-4000}" +IP_SANKOFA_PUBLIC_WEB="${IP_SANKOFA_PUBLIC_WEB:-$IP_SANKOFA_PORTAL}" +SANKOFA_PUBLIC_WEB_PORT="${SANKOFA_PUBLIC_WEB_PORT:-$SANKOFA_PORTAL_PORT}" IP_ORDER_HAPROXY="${IP_ORDER_HAPROXY:-192.168.11.39}" THE_ORDER_UPSTREAM_IP="${THE_ORDER_UPSTREAM_IP:-${IP_ORDER_HAPROXY}}" THE_ORDER_UPSTREAM_PORT="${THE_ORDER_UPSTREAM_PORT:-80}" @@ -41,8 +43,8 @@ SANKOFA_NPM_ID_WWW_THE_ORDER="${SANKOFA_NPM_ID_WWW_THE_ORDER:-59}" # Optional 4th field: canonical HTTPS apex — NPM advanced_config 301 (www → apex). Matches update-npmplus-proxy-hosts-api.sh. declare -A PROXY_HOSTS=( - ["$SANKOFA_NPM_ID_ROOT"]="sankofa.nexus|${IP_SANKOFA_PORTAL}|${SANKOFA_PORTAL_PORT}|" - ["$SANKOFA_NPM_ID_WWW"]="www.sankofa.nexus|${IP_SANKOFA_PORTAL}|${SANKOFA_PORTAL_PORT}|https://sankofa.nexus" + ["$SANKOFA_NPM_ID_ROOT"]="sankofa.nexus|${IP_SANKOFA_PUBLIC_WEB}|${SANKOFA_PUBLIC_WEB_PORT}|" + ["$SANKOFA_NPM_ID_WWW"]="www.sankofa.nexus|${IP_SANKOFA_PUBLIC_WEB}|${SANKOFA_PUBLIC_WEB_PORT}|https://sankofa.nexus" ["$SANKOFA_NPM_ID_PHOENIX"]="phoenix.sankofa.nexus|${IP_SANKOFA_PHOENIX_API}|${SANKOFA_PHOENIX_API_PORT}|" ["$SANKOFA_NPM_ID_WWW_PHOENIX"]="www.phoenix.sankofa.nexus|${IP_SANKOFA_PHOENIX_API}|${SANKOFA_PHOENIX_API_PORT}|https://phoenix.sankofa.nexus" ["$SANKOFA_NPM_ID_THE_ORDER"]="the-order.sankofa.nexus|${THE_ORDER_UPSTREAM_IP}|${THE_ORDER_UPSTREAM_PORT}|"