feat(omnl-banking-portal): hub/bridge routes, hash anchors, and /omnl nginx proxy
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m22s
CI/CD Pipeline / Security Scanning (push) Successful in 2m40s
CI/CD Pipeline / Lint and Format (push) Failing after 50s
CI/CD Pipeline / Terraform Validation (push) Failing after 27s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 28s
Validation / validate-genesis (push) Successful in 36s
Validation / validate-terraform (push) Failing after 36s
Validation / validate-kubernetes (push) Failing after 13s
Validation / validate-smart-contracts (push) Failing after 13s
Validation / validate-security (push) Failing after 1m33s
Validation / validate-documentation (push) Failing after 24s

Add DBIS Hub landing page with /hub and /bridge routes, central-bank hash scroll targets,
nginx /omnl/ proxy to token-aggregation for local dev and production hosts, and a
public-url-zardasht tunnel URL template for operator handoff.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-07-01 01:41:42 -07:00
parent cad927816e
commit a748a504f5
11 changed files with 250 additions and 113 deletions

View File

@@ -63,6 +63,13 @@ server {
proxy_read_timeout 120s;
}
# OMNL operator HTML consoles (token-aggregation)
location /omnl/ {
proxy_pass http://omnl_api/omnl/;
proxy_set_header Host $host;
proxy_read_timeout 120s;
}
location /health {
return 200 'OMNL Bank online — 128 chains';
add_header Content-Type text/plain;

View File

@@ -33,6 +33,18 @@ location /exchange/ {
proxy_read_timeout 120s;
}
# token-aggregation HTML consoles (/omnl/compliance, /omnl/dashboard, /omnl/terminal)
location /omnl/ {
include /etc/nginx/omnl-portal-auth-current.conf.inc;
proxy_pass http://omnl_api/omnl/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
}
location /health {
return 200 'OMNL Bank online — 128 chains';
add_header Content-Type text/plain;

View File

@@ -56,6 +56,14 @@ http {
proxy_read_timeout 120s;
}
location /omnl/ {
include /srv/zardasht-portal/deploy/nginx/omnl-portal-auth-current.conf.inc;
proxy_pass http://omnl_lxc_api/omnl/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_read_timeout 120s;
}
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";

View File

@@ -0,0 +1,6 @@
# Copy to logs/public-url-zardasht.txt after cloudflared quick tunnel starts (logs/ is gitignored).
# Example: echo "https://YOUR-SUBDOMAIN.trycloudflare.com" > logs/public-url-zardasht.txt
#
# Institutional share: use production FQDNs from config/omnl-banking-portal-link-manifest.v1.json
# Entry path for demo tunnel: /hub
https://YOUR-SUBDOMAIN.trycloudflare.com

View File

@@ -15,6 +15,7 @@ import HistoryPage from './pages/HistoryPage'
import AdminPanel from './pages/AdminPanel'
import DocsPage from './pages/DocsPage'
import WalletsDemoPage from './pages/WalletsDemoPage'
import HubPage from './pages/HubPage'
import Layout from './components/layout/Layout'
import OmnlProductLayout from './components/layout/OmnlProductLayout'
import ToastProvider from './components/ui/ToastProvider'
@@ -49,6 +50,7 @@ function App() {
<AdminProvider>
<Routes>
<Route element={<OmnlProductLayout />}>
<Route path="/hub" element={<HubPage />} />
<Route path="/central-bank" element={<CentralBankPage />} />
<Route path="/office-24" element={<Office24Page />} />
<Route path="/trade" element={<TradePage />} />
@@ -62,6 +64,7 @@ function App() {
}
>
<Route path="/" element={<BridgePage />} />
<Route path="/bridge" element={<BridgePage />} />
<Route path="/reserve" element={<ReservePage />} />
<Route path="/history" element={<HistoryPage />} />
<Route path="/admin" element={<AdminPanel />} />

View File

@@ -26,9 +26,9 @@ export default function Layout({ children }: LayoutProps) {
</Link>
<div className="hidden sm:flex gap-1">
<Link
to="/"
to="/bridge"
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
isActive('/') ? 'bg-teal-600 text-white' : 'text-[#A0A0A0] hover:text-white hover:bg-white/5'
isActive('/bridge') || isActive('/') ? 'bg-teal-600 text-white' : 'text-[#A0A0A0] hover:text-white hover:bg-white/5'
}`}
>
Bridge

View File

@@ -12,6 +12,8 @@ import NasaIcon, { type NasaIconName } from '../icons/NasaIcon';
const PRODUCTS: { path: string; label: string; icon: NasaIconName }[] = [
{ path: '/hub', label: 'Hub', icon: 'satellite' },
{ path: '/central-bank', label: 'Central Bank', icon: 'central-bank' },
{ path: '/office-24', label: 'Office 24', icon: 'office' },
@@ -46,7 +48,7 @@ export default function OmnlProductLayout() {
<div className="max-w-[1600px] mx-auto px-4 h-14 flex items-center gap-4">
<Link to="/central-bank" className="flex items-center gap-2 shrink-0">
<Link to="/hub" className="flex items-center gap-2 shrink-0">
<span className="w-8 h-8 border border-[#2d6bb8] bg-[#163a6b] text-[#00d4ff] font-bold flex items-center justify-center text-sm">
@@ -88,7 +90,7 @@ export default function OmnlProductLayout() {
<Link
to="/"
to="/bridge"
className="nasa-nav-link hidden md:inline shrink-0 !border-transparent"

View File

@@ -114,6 +114,8 @@ export default function CentralBankDashboard() {
<section id="z-full-settlement" className="scroll-mt-20">
<h2 className="dash-section-title mb-1 flex items-center gap-2">
<NasaIcon name="money" size={14} className="nasa-icon--telemetry" />
@@ -144,8 +146,14 @@ export default function CentralBankDashboard() {
</section>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div id="z-settlement-protocols" className="scroll-mt-20">
<DashboardSection
title="Settlement service"
@@ -216,6 +224,10 @@ export default function CentralBankDashboard() {
</div>
<DashboardSection title="General ledger map" subtitle={`Money layers · Office ${office?.officeId ?? 24}`} icon="ledger">
<div className="flex flex-wrap gap-2 mb-4">

View File

@@ -1,109 +1,109 @@
import { useEffect, useState } from 'react';
import { fetchOmnlChainRegistry, type ChainRegistryResponse, type OmnlChainSummary } from '../../config/supported-chains';
import NasaIcon, { StatusIcon } from '../../components/icons/NasaIcon';
type Filter = 'all' | 'active' | 'primary' | 'l2';
export default function ChainMatrixPanel() {
const [registry, setRegistry] = useState<ChainRegistryResponse | null>(null);
const [filter, setFilter] = useState<Filter>('active');
const [search, setSearch] = useState('');
useEffect(() => {
fetchOmnlChainRegistry().then(setRegistry);
}, []);
const chains = (registry?.chains ?? []).filter((c) => {
if (filter === 'active' && c.status !== 'active') return false;
if (filter === 'primary' && c.omnlRole !== 'settlement-hub' && c.omnlRole !== 'mirror-target') return false;
if (filter === 'l2' && c.tier !== 'l2') return false;
if (search) {
const q = search.toLowerCase();
return c.name.toLowerCase().includes(q) || String(c.chainId).includes(q) || c.nativeSymbol.toLowerCase().includes(q);
}
return true;
});
return (
<section className="dash-card cb-card lg:col-span-2">
<div className="flex flex-wrap items-center justify-between gap-3 mb-4">
<div>
<h2 className="dash-section-title flex items-center gap-2">
<NasaIcon name="network" size={14} className="nasa-icon--telemetry" />
128-chain network matrix
</h2>
<p className="text-xs text-[#7a9bb8] font-mono mt-1">
{registry?.stats?.active ?? 0} active · {registry?.stats?.staged ?? 0} staged ·{' '}
{registry?.stats?.reserved ?? 0} reserved · primary {registry?.primaryChainId ?? 138}
</p>
</div>
<div className="flex items-center gap-2">
<NasaIcon name="search" size={14} className="nasa-icon--telemetry" />
<input
type="search"
placeholder="Search chain…"
value={search}
onChange={(e) => setSearch(e.target.value)}
className="nasa-input !min-h-[36px] !w-48 text-sm"
/>
</div>
</div>
<div className="flex gap-2 mb-4 flex-wrap">
{(['all', 'active', 'primary', 'l2'] as Filter[]).map((f) => (
<button
key={f}
import { useEffect, useState } from 'react';
import { fetchOmnlChainRegistry, type ChainRegistryResponse, type OmnlChainSummary } from '../../config/supported-chains';
import NasaIcon, { StatusIcon } from '../../components/icons/NasaIcon';
type Filter = 'all' | 'active' | 'primary' | 'l2';
export default function ChainMatrixPanel() {
const [registry, setRegistry] = useState<ChainRegistryResponse | null>(null);
const [filter, setFilter] = useState<Filter>('active');
const [search, setSearch] = useState('');
useEffect(() => {
fetchOmnlChainRegistry().then(setRegistry);
}, []);
const chains = (registry?.chains ?? []).filter((c) => {
if (filter === 'active' && c.status !== 'active') return false;
if (filter === 'primary' && c.omnlRole !== 'settlement-hub' && c.omnlRole !== 'mirror-target') return false;
if (filter === 'l2' && c.tier !== 'l2') return false;
if (search) {
const q = search.toLowerCase();
return c.name.toLowerCase().includes(q) || String(c.chainId).includes(q) || c.nativeSymbol.toLowerCase().includes(q);
}
return true;
});
return (
<section id="z-settlement-protocols-chains" className="dash-card cb-card lg:col-span-2 scroll-mt-20">
<div className="flex flex-wrap items-center justify-between gap-3 mb-4">
<div>
<h2 className="dash-section-title flex items-center gap-2">
<NasaIcon name="network" size={14} className="nasa-icon--telemetry" />
128-chain network matrix
</h2>
<p className="text-xs text-[#7a9bb8] font-mono mt-1">
{registry?.stats?.active ?? 0} active · {registry?.stats?.staged ?? 0} staged ·{' '}
{registry?.stats?.reserved ?? 0} reserved · primary {registry?.primaryChainId ?? 138}
</p>
</div>
<div className="flex items-center gap-2">
<NasaIcon name="search" size={14} className="nasa-icon--telemetry" />
<input
type="search"
placeholder="Search chain…"
value={search}
onChange={(e) => setSearch(e.target.value)}
className="nasa-input !min-h-[36px] !w-48 text-sm"
/>
</div>
</div>
<div className="flex gap-2 mb-4 flex-wrap">
{(['all', 'active', 'primary', 'l2'] as Filter[]).map((f) => (
<button
key={f}
type="button"
onClick={() => setFilter(f)}
className={`px-3 py-1 text-xs capitalize font-mono border ${
filter === f
? 'border-[#00d4ff] text-[#00d4ff] bg-[#00d4ff]/10'
: 'border-[#1e4a8a] text-[#7a9bb8]'
}`}
>
{f}
</button>
))}
</div>
<div className="overflow-x-auto max-h-[360px] overflow-y-auto">
<table className="w-full text-sm nasa-table">
<thead className="sticky top-0">
<tr className="text-left">
<th className="py-2 pr-3">ID</th>
<th className="py-2 pr-3">Network</th>
<th className="py-2 pr-3">Role</th>
<th className="py-2 pr-3">Status</th>
<th className="py-2 pr-3">Swap</th>
<th className="py-2">Bridge</th>
</tr>
</thead>
<tbody>
{chains.map((c: OmnlChainSummary) => (
<tr key={c.chainId}>
<td className="py-1.5 pr-3 font-mono text-[#00d4ff]">{c.chainId}</td>
<td className="py-1.5 pr-3 text-[#e8f4ff]">{c.name}</td>
<td className="py-1.5 pr-3 text-xs text-[#7a9bb8]">{c.omnlRole}</td>
<td className="py-1.5 pr-3">
<span
className={`text-xs px-1.5 py-0.5 font-mono uppercase ${
c.status === 'active'
? 'text-[#3dff8a] border border-[#3dff8a]/35'
: c.status === 'staged'
? 'text-[#ffb347] border border-[#ffb347]/35'
: 'text-[#7a9bb8] border border-[#7a9bb8]/35'
}`}
>
{c.status}
</span>
</td>
<td className="py-1.5 pr-3"><StatusIcon ok={!!c.settlement?.swapEnabled} /></td>
<td className="py-1.5"><StatusIcon ok={!!c.settlement?.bridgeEnabled} /></td>
</tr>
))}
</tbody>
</table>
</div>
</section>
);
}

View File

@@ -1,5 +1,16 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import CentralBankDashboard from '../features/central-bank/CentralBankDashboard';
export default function CentralBankPage() {
const { hash } = useLocation();
useEffect(() => {
if (!hash) return;
const id = hash.replace(/^#/, '');
const el = document.getElementById(id);
el?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}, [hash]);
return <CentralBankDashboard />;
}

View File

@@ -0,0 +1,76 @@
import { Link } from 'react-router-dom';
import NasaIcon, { type NasaIconName } from '../components/icons/NasaIcon';
const BANKING: { path: string; label: string; icon: NasaIconName }[] = [
{ path: '/central-bank', label: 'Z Online Bank / Central Bank', icon: 'central-bank' },
{ path: '/central-bank#z-full-settlement', label: 'M0→M4 settlement', icon: 'money' },
{ path: '/central-bank#z-settlement-protocols', label: 'Wire & chain protocols', icon: 'network' },
{ path: '/central-bank#z-settlement-protocols-chains', label: '128-chain matrix', icon: 'network' },
{ path: '/office-24', label: 'Office 24', icon: 'office' },
{ path: '/trade', label: 'DBIS Trade', icon: 'trade' },
{ path: '/swap', label: 'Digital Swap', icon: 'swap' },
];
const CHAIN138: { path: string; label: string; icon: NasaIconName }[] = [
{ path: '/bridge', label: 'Bridge', icon: 'bridge' },
{ path: '/reserve', label: 'Reserve', icon: 'ledger' },
{ path: '/history', label: 'History', icon: 'chart' },
{ path: '/wallets', label: 'Wallets', icon: 'globe' },
{ path: '/docs', label: 'Developers / API docs', icon: 'link' },
{ path: '/admin', label: 'Admin', icon: 'satellite' },
];
const OMNL_OPS: { path: string; label: string; icon: NasaIconName }[] = [
{ path: '/omnl/compliance', label: 'OMNL Compliance console', icon: 'ledger' },
{ path: '/omnl/dashboard', label: 'OMNL API snapshot', icon: 'chart' },
{ path: '/omnl/terminal', label: 'Settlement terminal', icon: 'rail' },
];
function LinkGrid({
title,
items,
}: {
title: string;
items: { path: string; label: string; icon: NasaIconName }[];
}) {
return (
<section className="dash-card cb-card p-4">
<h2 className="dash-section-title mb-3">{title}</h2>
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-2">
{items.map(({ path, label, icon }) => (
<li key={path}>
<Link
to={path}
className="dash-quick-links__chip inline-flex w-full !justify-start gap-2 py-2.5 px-3"
>
<NasaIcon name={icon} size={14} />
{label}
</Link>
</li>
))}
</ul>
</section>
);
}
export default function HubPage() {
return (
<div className="dash-page cb-page max-w-[1400px] mx-auto px-4 py-6">
<header className="mb-8">
<p className="text-xs font-mono text-[#7a9bb8] mb-1">DBIS · OMNL · Chain 138</p>
<h1 className="text-2xl font-semibold text-[#e8f4ff] flex items-center gap-2">
<NasaIcon name="satellite" size={22} className="nasa-icon--telemetry" />
DBIS Hub
</h1>
<p className="text-sm text-[#7a9bb8] mt-2 max-w-2xl">
Mission control entry banking portals, settlement surfaces, and Chain 138 tools on this host.
</p>
</header>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-4">
<LinkGrid title="Banking & settlement" items={BANKING} />
<LinkGrid title="Chain 138 & operators" items={CHAIN138} />
</div>
<LinkGrid title="OMNL operator consoles" items={OMNL_OPS} />
</div>
);
}