From 9676f22a8ef3ea028a8601914db7cfa6ca93d76c Mon Sep 17 00:00:00 2001 From: AdrianoDev Date: Mon, 27 Apr 2026 17:33:32 +0200 Subject: [PATCH] chore: import gateway, exchange secrets, env example --- .env.example | 7 +++ gateway/Caddyfile | 36 ++++++++++++++ gateway/public/index.html | 97 ++++++++++++++++++++++++++++++++++++ gateway/public/status.js | 23 +++++++++ gateway/public/style.css | 101 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 264 insertions(+) create mode 100644 .env.example create mode 100644 gateway/Caddyfile create mode 100644 gateway/public/index.html create mode 100644 gateway/public/status.js create mode 100644 gateway/public/style.css diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..fb61814 --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +GATEWAY_PORT=8080 + +# Override ambiente per ogni MCP exchange (precedenza: env > secret > default) +DERIBIT_TESTNET=true +BYBIT_TESTNET=true +HYPERLIQUID_TESTNET=true +ALPACA_PAPER=true diff --git a/gateway/Caddyfile b/gateway/Caddyfile new file mode 100644 index 0000000..140b3d9 --- /dev/null +++ b/gateway/Caddyfile @@ -0,0 +1,36 @@ +{ + admin off + auto_https off +} + +:8080 { + log { + output stdout + format console + } + + handle_path /mcp-deribit/* { + reverse_proxy mcp-deribit:9011 + } + handle_path /mcp-hyperliquid/* { + reverse_proxy mcp-hyperliquid:9012 + } + handle_path /mcp-bybit/* { + reverse_proxy mcp-bybit:9019 + } + handle_path /mcp-alpaca/* { + reverse_proxy mcp-alpaca:9020 + } + handle_path /mcp-macro/* { + reverse_proxy mcp-macro:9013 + } + handle_path /mcp-sentiment/* { + reverse_proxy mcp-sentiment:9014 + } + + # Landing page statica + handle { + root * /srv + file_server + } +} diff --git a/gateway/public/index.html b/gateway/public/index.html new file mode 100644 index 0000000..ee81341 --- /dev/null +++ b/gateway/public/index.html @@ -0,0 +1,97 @@ + + + + +Cerbero — MCP gateway + + + +
+

Cerbero

+

Sistema trading autonomo crypto, architettura MCP-only.

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
StatoServizioPorta int.DescrizioneLink
mcp-memory9015Store L1/L2, system prompt base + dynhealth · docs
mcp-scheduler9016Recurring task + core agent runnerhealth · docs
mcp-deribit9011Options testnet order/markethealth · docs
mcp-hyperliquid9012Perp DEX testnethealth · docs
mcp-macro9013FRED indicators + Finnhub calendarhealth · docs
mcp-sentiment9014CryptoPanic news feedhealth · docs
mcp-telegram9017Bot commands + notifiche operatorehealth · docs
mcp-portfolio9018Holdings + yfinance + UI htmxhealth · gui · docs
+ +
+

Console operativa

+

/console — run del core agent, eventi stdout/stderr, L1 live, trigger manuale.

+
+
+ + + + + + diff --git a/gateway/public/status.js b/gateway/public/status.js new file mode 100644 index 0000000..0d548b0 --- /dev/null +++ b/gateway/public/status.js @@ -0,0 +1,23 @@ +const rows = document.querySelectorAll("tr[data-path]"); + +async function poll() { + for (const row of rows) { + const dot = row.querySelector(".status"); + try { + const r = await fetch(`${row.dataset.path}/health`, { + method: "GET", + cache: "no-store", + }); + dot.classList.toggle("ok", r.ok); + dot.classList.toggle("err", !r.ok); + dot.setAttribute("aria-label", r.ok ? "ok" : "error"); + } catch { + dot.classList.remove("ok"); + dot.classList.add("err"); + dot.setAttribute("aria-label", "unreachable"); + } + } +} + +poll(); +setInterval(poll, 5000); diff --git a/gateway/public/style.css b/gateway/public/style.css new file mode 100644 index 0000000..a650f40 --- /dev/null +++ b/gateway/public/style.css @@ -0,0 +1,101 @@ +:root { + --bg: #0f172a; + --fg: #e2e8f0; + --muted: #94a3b8; + --card: #1e293b; + --border: #334155; + --ok: #22c55e; + --err: #ef4444; + --unknown: #64748b; + --accent: #38bdf8; +} + +* { box-sizing: border-box; } + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + background: var(--bg); + color: var(--fg); + line-height: 1.5; +} + +header, main, footer { + max-width: 960px; + margin: 0 auto; + padding: 1.5rem; +} + +header h1 { + margin: 0 0 0.25rem; + color: var(--accent); + font-size: 2rem; +} + +header p { + margin: 0; + color: var(--muted); +} + +table { + width: 100%; + border-collapse: collapse; + background: var(--card); + border-radius: 8px; + overflow: hidden; +} + +th, td { + padding: 0.75rem 1rem; + text-align: left; + border-bottom: 1px solid var(--border); +} + +th { + background: #0f172a; + color: var(--muted); + font-weight: 600; + font-size: 0.85rem; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +tr:last-child td { border-bottom: none; } + +td:nth-child(3) { + font-family: ui-monospace, "SF Mono", Menlo, monospace; + color: var(--muted); +} + +a { + color: var(--accent); + text-decoration: none; + margin-right: 0.5rem; +} + +a:hover { text-decoration: underline; } + +.status { + display: inline-block; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--unknown); + transition: background 0.3s ease; +} + +.status.ok { background: var(--ok); box-shadow: 0 0 8px var(--ok); } +.status.err { background: var(--err); box-shadow: 0 0 8px var(--err); } + +footer { + color: var(--muted); + font-size: 0.85rem; + margin-top: 2rem; +} + +code { + background: var(--border); + padding: 0.1rem 0.3rem; + border-radius: 3px; + font-size: 0.9em; +}