85 lines
7 KiB
JavaScript
85 lines
7 KiB
JavaScript
// trust-panel.js — the TRUST TOPOLOGY MAP. For the trust-nobody, non-technical user: a VISUAL diagram of where
|
|
// their data goes (You → quantum tunnel → self-hosted server → back), what's protecting it, what is NOT collected,
|
|
// and an honest AI disclaimer. Self-contained inline SVG + styles (light+dark), no CDN.
|
|
(function () {
|
|
const PROTO_URL = 'https://datatracker.ietf.org/doc/draft-kwiatkowski-tls-ecdhe-mlkem/'
|
|
function injectStyles() {
|
|
if (document.getElementById('trust-panel-styles')) return
|
|
const s = document.createElement('style'); s.id = 'trust-panel-styles'
|
|
s.textContent = [
|
|
'.tp-overlay{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:9999;display:flex;align-items:flex-end;justify-content:center}',
|
|
'.tp-modal{width:100%;max-width:520px;max-height:90dvh;overflow-y:auto;background:#16181d;color:#eef1f4;border:1px solid #2a2e35}',
|
|
'html[data-theme="light"] .tp-modal{background:#fff;color:#141821;border-color:#e2e5ea}',
|
|
'.tp-head{display:flex;justify-content:space-between;align-items:center;padding:13px 16px;border-bottom:1px solid #2a2e35;position:sticky;top:0;background:inherit;font-weight:700}',
|
|
'html[data-theme="light"] .tp-head{border-color:#e2e5ea}',
|
|
'.tp-x{background:none;border:0;color:inherit;font-size:1.5rem;line-height:1;cursor:pointer;opacity:.55}',
|
|
'.tp-sec{padding:14px 16px;border-bottom:1px solid #2a2e35}',
|
|
'html[data-theme="light"] .tp-sec{border-color:#eef0f3}',
|
|
'.tp-sec:last-child{border-bottom:0}',
|
|
'.tp-h{font-weight:700;font-size:.82rem;margin-bottom:6px}',
|
|
'.tp-p{font-size:.8rem;opacity:.82;line-height:1.5}',
|
|
'.tp-map{width:100%;margin:2px 0 6px}',
|
|
'.tp-node text{font-size:9px;font-weight:700}',
|
|
'.tp-badge{display:inline-block;font-family:ui-monospace,monospace;font-weight:700;font-size:.76rem;color:#4f9bf0}',
|
|
'html[data-theme="light"] .tp-badge{color:#2563eb}',
|
|
'.tp-link{color:#4f9bf0;font-size:.78rem;text-decoration:none;border-bottom:1px solid currentColor}',
|
|
'html[data-theme="light"] .tp-link{color:#2563eb}',
|
|
'.tp-list{list-style:none;padding:0;margin:6px 0 0}',
|
|
'.tp-list li{font-size:.8rem;line-height:1.5;padding-left:18px;position:relative;opacity:.85}',
|
|
'.tp-list li::before{content:"\\2713";position:absolute;left:0;color:#3fb27f;font-weight:700}',
|
|
'.tp-warn{background:rgba(224,180,60,.12);border:1px solid #d8a83c;color:#e0b43c;padding:9px 11px;font-size:.78rem;line-height:1.5}',
|
|
].join('')
|
|
document.head.appendChild(s)
|
|
}
|
|
const esc = (s) => { const d = document.createElement('div'); d.textContent = s; return d.innerHTML }
|
|
async function pqStatus() { try { const r = await fetch('/pq-attest', { cache: 'no-store' }); if (!r.ok) return { pq: null }; return await r.json() } catch (_) { return { pq: null } } }
|
|
|
|
// the topology SVG: You ── tunnel ── Server ── tunnel ── You (round trip), self-contained, theme-aware via currentColor.
|
|
function topologySVG() {
|
|
const acc = '#4f9bf0', ok = '#3fb27f', mut = 'currentColor'
|
|
return '<svg class="tp-map" viewBox="0 0 320 150" fill="none">' +
|
|
// device (left)
|
|
'<g class="tp-node"><rect x="8" y="55" width="70" height="40" rx="4" fill="none" stroke="'+acc+'" stroke-width="1.5"/>' +
|
|
'<text x="43" y="72" text-anchor="middle" fill="'+mut+'">YOUR</text><text x="43" y="84" text-anchor="middle" fill="'+mut+'">DEVICE</text></g>' +
|
|
// server (right)
|
|
'<g class="tp-node"><rect x="242" y="55" width="70" height="40" rx="4" fill="none" stroke="'+ok+'" stroke-width="1.5"/>' +
|
|
'<text x="277" y="70" text-anchor="middle" fill="'+mut+'">SELF-HOSTED</text><text x="277" y="82" text-anchor="middle" fill="'+mut+'">SERVER</text></g>' +
|
|
// tunnel forward (top arrow)
|
|
'<path d="M80 66 L240 66" stroke="'+acc+'" stroke-width="1.5"/><path d="M234 62 L240 66 L234 70" stroke="'+acc+'" stroke-width="1.5"/>' +
|
|
'<text x="160" y="40" text-anchor="middle" class="tp-node" fill="'+acc+'">quantum tunnel →</text>' +
|
|
// tunnel back (bottom arrow)
|
|
'<path d="M240 84 L80 84" stroke="'+ok+'" stroke-width="1.5"/><path d="M86 80 L80 84 L86 88" stroke="'+ok+'" stroke-width="1.5"/>' +
|
|
'<text x="160" y="112" text-anchor="middle" class="tp-node" fill="'+ok+'">← quantum tunnel</text>' +
|
|
'</svg>'
|
|
}
|
|
|
|
async function openPanel() {
|
|
injectStyles()
|
|
const st = await pqStatus()
|
|
const usesAI = document.body.dataset.usesAi === '1' || true // most sessions here run an AI; show the honest disclaimer
|
|
const wrap = document.createElement('div'); wrap.className = 'tp-overlay'
|
|
wrap.innerHTML =
|
|
'<div class="tp-modal">' +
|
|
'<div class="tp-head"><span>How your data is protected</span><button class="tp-x">×</button></div>' +
|
|
// 1. the topology map
|
|
'<div class="tp-sec"><div class="tp-h">Where your data goes</div>' + topologySVG() +
|
|
'<div class="tp-p">Your data travels to a server and back — both directions through a quantum tunnel. The server is <b>self-hosted</b> by whoever shared this link.</div></div>' +
|
|
// 2. your browser's built-in PQ
|
|
'<div class="tp-sec"><div class="tp-h">Your browser already protects you</div>' +
|
|
'<div class="tp-p">Your browser has built-in <span class="tp-badge">X25519MLKEM768</span> — post-quantum encryption. It is designed to resist quantum computers \u2014 the kind of attack that breaks today\u2019s classical encryption. It is the industry\u2019s current standard, used by Cloudflare, Google and Chrome by default (NIST FIPS 203).</div>' +
|
|
'<div style="margin-top:6px"><a class="tp-link" href="'+PROTO_URL+'" target="_blank" rel="noopener">Read the protocol</a></div></div>' +
|
|
// 3. what we DON'T collect
|
|
'<div class="tp-sec"><div class="tp-h">What we do NOT collect</div><ul class="tp-list">' +
|
|
'<li>Your IP address is not stored.</li><li>No fingerprint of your browser or device.</li>' +
|
|
'<li>Nothing personally identifying — only the answers you choose to type.</li>' +
|
|
'<li>Your data is severed from your identity on our end.</li></ul></div>' +
|
|
// 4. honest AI disclaimer (conditional)
|
|
(usesAI ? '<div class="tp-sec"><div class="tp-warn"><b>This form uses AI.</b> Your answers travel over the quantum tunnel, but they are sent to an AI provider that generates the questions. That provider is not under your name or account — you are safer, but not zero-exposure. Not every Quantum Form uses AI.</div></div>' : '') +
|
|
'</div>'
|
|
document.body.appendChild(wrap)
|
|
wrap.querySelector('.tp-x').addEventListener('click', () => wrap.remove())
|
|
wrap.addEventListener('click', (e) => { if (e.target === wrap) wrap.remove() })
|
|
}
|
|
function wire() { document.querySelectorAll('.e2e, .h-neon').forEach(el => { el.style.cursor = 'pointer'; el.title = 'Tap to see how your data is protected'; el.addEventListener('click', openPanel) }) }
|
|
if (document.readyState !== 'loading') wire(); else document.addEventListener('DOMContentLoaded', wire)
|
|
})();
|