aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonzero Build System <builds@monzero.org>2026-08-15 22:02:03 +0100
committerMonzero Build System <builds@monzero.org>2026-08-15 22:02:03 +0100
commit30cce40b48e86bb92146378a1a8178d774d65a71 (patch)
treeccf51422964f817a1a9e8efee1094903d5aef928
parent310832fe087427fc01af504821190c2482a438d7 (diff)
downloadmonzero-core-30cce40b48e86bb92146378a1a8178d774d65a71.tar.gz
monzero-core-30cce40b48e86bb92146378a1a8178d774d65a71.tar.xz
monzero-core-30cce40b48e86bb92146378a1a8178d774d65a71.zip
Launch gated Monzero Studio beta
-rw-r--r--deploy/code-host/README.md5
-rw-r--r--deploy/code-host/nginx-app.monzero.org.conf27
-rw-r--r--website/app/app.js26
-rw-r--r--website/app/index.html105
-rw-r--r--website/app/styles.css1
-rw-r--r--website/index.html3
6 files changed, 166 insertions, 1 deletions
diff --git a/deploy/code-host/README.md b/deploy/code-host/README.md
index a6a2827d9..581986b2c 100644
--- a/deploy/code-host/README.md
+++ b/deploy/code-host/README.md
@@ -14,3 +14,8 @@ Nginx exposes only `git-upload-pack`; it intentionally has no
`git-receive-pack` route. After deployment, Certbot upgrades the virtual host
to HTTPS and UFW admits ports 80 and 443.
+`nginx-app.monzero.org.conf` separately serves the static Studio beta from
+`/var/www/monzero-app` and exposes only the daemon's restricted `get_info`
+method through a same-origin status endpoint. Token, NFT, collection, and swap
+submission controls remain disabled until reviewed consensus and wallet RPC
+support exists.
diff --git a/deploy/code-host/nginx-app.monzero.org.conf b/deploy/code-host/nginx-app.monzero.org.conf
new file mode 100644
index 000000000..776489630
--- /dev/null
+++ b/deploy/code-host/nginx-app.monzero.org.conf
@@ -0,0 +1,27 @@
+server {
+ listen 80;
+ listen [::]:80;
+ server_name app.monzero.org;
+
+ root /var/www/monzero-app;
+ index index.html;
+
+ add_header X-Content-Type-Options nosniff always;
+ add_header X-Frame-Options DENY always;
+ add_header Referrer-Policy no-referrer always;
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
+ add_header Content-Security-Policy "default-src 'self'; connect-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'" always;
+
+ location = /api/node/get_info {
+ limit_except POST { deny all; }
+ proxy_pass http://127.0.0.1:6175/get_info;
+ proxy_set_header Content-Type application/json;
+ proxy_set_header Host 127.0.0.1;
+ proxy_connect_timeout 2s;
+ proxy_read_timeout 5s;
+ }
+
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+}
diff --git a/website/app/app.js b/website/app/app.js
new file mode 100644
index 000000000..ea1791839
--- /dev/null
+++ b/website/app/app.js
@@ -0,0 +1,26 @@
+const tabs = [...document.querySelectorAll('[role="tab"]')];
+const panels = [...document.querySelectorAll('[role="tabpanel"]')];
+
+tabs.forEach((tab) => tab.addEventListener('click', () => {
+ tabs.forEach((item) => item.setAttribute('aria-selected', String(item === tab)));
+ panels.forEach((panel) => {
+ const active = panel.id === tab.dataset.panel;
+ panel.hidden = !active;
+ panel.classList.toggle('active', active);
+ });
+}));
+
+fetch('/api/node/get_info', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: '{}'
+}).then((response) => {
+ if (!response.ok) throw new Error('node unavailable');
+ return response.json();
+}).then((info) => {
+ document.querySelector('#node-state').textContent = info.status === 'OK' ? 'Online' : info.status;
+ document.querySelector('#height').textContent = Number(info.height).toLocaleString();
+ document.querySelector('#peers').textContent = Number(info.incoming_connections_count || 0) + Number(info.outgoing_connections_count || 0);
+}).catch(() => {
+ document.querySelector('#node-state').textContent = 'Unavailable';
+});
diff --git a/website/app/index.html b/website/app/index.html
new file mode 100644
index 000000000..50fd1af63
--- /dev/null
+++ b/website/app/index.html
@@ -0,0 +1,105 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="description" content="Monzero beta asset, NFT and non-custodial swap workspace.">
+ <meta name="theme-color" content="#07110f">
+ <title>Monzero Studio — Assets, NFTs and swaps</title>
+ <link rel="stylesheet" href="styles.css">
+</head>
+<body>
+ <header>
+ <a class="brand" href="https://monzero.org"><span>MZ</span> MONZERO</a>
+ <nav aria-label="Product navigation">
+ <a href="https://monzero.org/explorer/">Explorer</a>
+ <a href="https://code.monzero.org/">Source</a>
+ <a href="https://monzero.org">Network</a>
+ </nav>
+ </header>
+
+ <main>
+ <section class="intro">
+ <div>
+ <p class="eyebrow">Protocol laboratory · Testnet first</p>
+ <h1>Create without pretending.</h1>
+ <p>Monzero Studio is the future non-custodial workspace for fixed-supply tokens, NFTs, collections and atomic swaps. Transaction submission stays locked until the corresponding consensus and wallet code is available.</p>
+ </div>
+ <aside class="network-card">
+ <span class="label">Current public node</span>
+ <strong id="node-state">Checking…</strong>
+ <dl>
+ <div><dt>Height</dt><dd id="height">—</dd></div>
+ <div><dt>Peers</dt><dd id="peers">—</dd></div>
+ <div><dt>Asset protocol</dt><dd class="amber">Inactive</dd></div>
+ </dl>
+ </aside>
+ </section>
+
+ <section class="workspace">
+ <div class="tabs" role="tablist" aria-label="Studio tools">
+ <button role="tab" aria-selected="true" data-panel="token">Token creator</button>
+ <button role="tab" aria-selected="false" data-panel="nft">NFT studio</button>
+ <button role="tab" aria-selected="false" data-panel="collections">Collections</button>
+ <button role="tab" aria-selected="false" data-panel="swap">Atomic swap</button>
+ </div>
+
+ <article class="panel active" id="token" role="tabpanel">
+ <div class="panel-copy"><p class="eyebrow">Fixed supply</p><h2>Issue a token</h2><p>Supply is created once. No administrator mint, freeze, blacklist or clawback authority exists in V1.</p></div>
+ <form>
+ <label>Name <input type="text" maxlength="64" placeholder="Example Token" disabled></label>
+ <label>Symbol <input type="text" maxlength="12" placeholder="EXM" disabled></label>
+ <div class="split"><label>Atomic supply <input type="number" placeholder="1000000" disabled></label><label>Decimals <input type="number" min="0" max="11" placeholder="8" disabled></label></div>
+ <label>Metadata content hash <input type="text" placeholder="32-byte hash" disabled></label>
+ <button type="button" disabled>Awaiting testnet RPC</button>
+ </form>
+ </article>
+
+ <article class="panel" id="nft" role="tabpanel" hidden>
+ <div class="panel-copy"><p class="eyebrow">One indivisible unit</p><h2>Mint an NFT</h2><p>Only a bounded metadata hash is committed to consensus. Images and descriptions remain off-chain and untrusted.</p></div>
+ <form>
+ <label>Title <input type="text" maxlength="96" placeholder="Artifact 001" disabled></label>
+ <label>Collection ID <input type="text" placeholder="Optional collection asset ID" disabled></label>
+ <label>Metadata URI <input type="url" placeholder="ipfs://…" disabled></label>
+ <label>Metadata content hash <input type="text" placeholder="32-byte hash" disabled></label>
+ <button type="button" disabled>Awaiting testnet RPC</button>
+ </form>
+ </article>
+
+ <article class="panel" id="collections" role="tabpanel" hidden>
+ <div class="panel-copy"><p class="eyebrow">Authenticated membership</p><h2>Create a collection</h2><p>Members require a signature from the collection controller over the exact member asset ID.</p></div>
+ <form>
+ <label>Collection name <input type="text" maxlength="96" placeholder="Monzero Originals" disabled></label>
+ <label>Metadata URI <input type="url" placeholder="ipfs://…" disabled></label>
+ <label>Metadata content hash <input type="text" placeholder="32-byte hash" disabled></label>
+ <button type="button" disabled>Awaiting testnet RPC</button>
+ </form>
+ </article>
+
+ <article class="panel" id="swap" role="tabpanel" hidden>
+ <div class="panel-copy"><p class="eyebrow">Non-custodial only</p><h2>Atomic swap</h2><p>No deposits and no operator custody. Swap activation requires an audited protocol ensuring either both sides settle or neither side does.</p></div>
+ <form>
+ <div class="split"><label>You offer <input type="text" placeholder="Amount and asset ID" disabled></label><label>You request <input type="text" placeholder="Amount and asset ID" disabled></label></div>
+ <label>Counterparty or signed offer <input type="text" placeholder="Public offer payload" disabled></label>
+ <label>Expiry height <input type="number" placeholder="Block height" disabled></label>
+ <button type="button" disabled>Swap protocol not active</button>
+ </form>
+ </article>
+ </section>
+
+ <section class="gate">
+ <p class="eyebrow">Activation gates</p>
+ <h2>What remains before funds are accepted.</h2>
+ <ol>
+ <li><span>01</span>Persistent, reorg-safe asset state</li>
+ <li><span>02</span>Confidential ownership and conservation proofs</li>
+ <li><span>03</span>Wallet scanning, restoration and signing</li>
+ <li><span>04</span>Adversarial testnet and independent review</li>
+ </ol>
+ </section>
+ </main>
+
+ <footer><span>Monzero Studio beta</span><span>No deposits · No custody · No active issuance</span></footer>
+ <script src="app.js"></script>
+</body>
+</html>
diff --git a/website/app/styles.css b/website/app/styles.css
new file mode 100644
index 000000000..c90f26581
--- /dev/null
+++ b/website/app/styles.css
@@ -0,0 +1 @@
+:root{--bg:#06100e;--surface:#0b1815;--ink:#ebf7f2;--muted:#8da39b;--line:rgba(199,255,228,.14);--lime:#b9ff66;--mint:#62e7af;--amber:#e6ae59;--mono:ui-monospace,SFMono-Regular,Menlo,monospace;--sans:Inter,Manrope,system-ui,sans-serif}*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at 75% 10%,rgba(98,231,175,.11),transparent 30%),var(--bg);color:var(--ink);font-family:var(--sans);line-height:1.55}a{color:inherit}header{height:76px;padding:0 clamp(1.2rem,6vw,6rem);display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line)}.brand{text-decoration:none;font-size:.78rem;letter-spacing:.18em;font-weight:700}.brand span{display:inline-grid;place-items:center;width:31px;height:31px;margin-right:.65rem;border:1px solid var(--lime);border-radius:50%;color:var(--lime);font:600 .6rem var(--mono)}nav{display:flex;gap:2rem}nav a{color:var(--muted);font-size:.78rem;text-decoration:none}main{max-width:1440px;margin:auto;padding:clamp(3rem,7vw,7rem) clamp(1.2rem,6vw,6rem)}.intro{display:grid;grid-template-columns:1.2fr .65fr;gap:clamp(2rem,8vw,8rem);align-items:end;margin-bottom:5rem}.eyebrow{color:var(--mint);font:600 .64rem var(--mono);letter-spacing:.15em;text-transform:uppercase}h1{font-size:clamp(3.4rem,7vw,7.5rem);line-height:.9;letter-spacing:-.065em;margin:.8rem 0 1.8rem;max-width:900px;font-weight:520}.intro>div>p:last-child,.panel-copy p,.gate{color:var(--muted);max-width:720px}.network-card{border:1px solid var(--line);background:rgba(255,255,255,.025);padding:1.5rem}.label{display:block;color:var(--muted);font:.62rem var(--mono);text-transform:uppercase;letter-spacing:.1em}.network-card>strong{display:block;margin:.7rem 0 1.4rem;font:500 1.4rem var(--mono)}dl{margin:0;border-top:1px solid var(--line)}dl div{display:flex;justify-content:space-between;padding:.8rem 0;border-bottom:1px solid var(--line)}dt{color:var(--muted);font-size:.75rem}dd{margin:0;font:.75rem var(--mono)}.amber{color:var(--amber)}.workspace{border:1px solid var(--line);background:rgba(255,255,255,.015)}.tabs{display:grid;grid-template-columns:repeat(4,1fr);border-bottom:1px solid var(--line)}.tabs button{padding:1.1rem;border:0;border-right:1px solid var(--line);background:transparent;color:var(--muted);font:600 .68rem var(--mono);cursor:pointer}.tabs button:last-child{border:0}.tabs button[aria-selected=true]{background:rgba(185,255,102,.08);color:var(--lime)}.panel{display:grid;grid-template-columns:.7fr 1.3fr;gap:5rem;padding:clamp(1.5rem,5vw,4.5rem)}h2{font-size:clamp(2rem,4vw,4rem);line-height:1;margin:.6rem 0 1rem;letter-spacing:-.045em;font-weight:520}form{display:grid;gap:1rem}label{display:grid;gap:.45rem;color:#b5c6c0;font-size:.72rem}input{width:100%;padding:.95rem 1rem;border:1px solid var(--line);background:#081310;color:var(--ink);font:inherit}input:disabled{opacity:.6}.split{display:grid;grid-template-columns:1fr 1fr;gap:1rem}form button{padding:1rem;border:0;background:#26342f;color:#71837c;font-weight:700;cursor:not-allowed}.gate{padding:6rem 0 2rem}.gate ol{list-style:none;margin:2rem 0 0;padding:0;border-top:1px solid var(--line)}.gate li{display:flex;gap:1.2rem;padding:1.1rem 0;border-bottom:1px solid var(--line);color:var(--ink)}.gate li span{color:var(--lime);font:.7rem var(--mono)}footer{display:flex;justify-content:space-between;padding:2rem clamp(1.2rem,6vw,6rem);border-top:1px solid var(--line);color:#657a72;font:.65rem var(--mono)}@media(max-width:800px){nav{display:none}.intro,.panel{grid-template-columns:1fr;gap:2rem}.tabs{grid-template-columns:1fr 1fr}.tabs button{border-bottom:1px solid var(--line)}.split{grid-template-columns:1fr}footer{flex-direction:column;gap:.5rem}}
diff --git a/website/index.html b/website/index.html
index b4c41f7a7..30069fe46 100644
--- a/website/index.html
+++ b/website/index.html
@@ -22,6 +22,7 @@
<a href="#network">Network</a>
<a href="#miners">Miners</a>
<a href="explorer/">Explorer</a>
+ <a href="https://app.monzero.org/" rel="noopener">Studio</a>
<a href="https://code.monzero.org/" rel="noopener">Source</a>
<a href="#about">About</a>
<a href="#mine">Mine</a>
@@ -202,7 +203,7 @@
<footer>
<a class="brand" href="#top"><span class="brand-mark">MZ</span><span>MONZERO</span></a>
<p>Experimental software. Verify everything.</p>
- <p>© <span id="year"></span> Monzero contributors · <a href="https://monzero.org">monzero.org</a> · <a href="https://code.monzero.org/" rel="noopener">Source code</a></p>
+ <p>© <span id="year"></span> Monzero contributors · <a href="https://monzero.org">monzero.org</a> · <a href="https://app.monzero.org/" rel="noopener">Studio</a> · <a href="https://code.monzero.org/" rel="noopener">Source code</a></p>
</footer>
<div class="toast" role="status" aria-live="polite">Copied to clipboard</div>