verae-mcp-timestamping-server/site/hasher.html
George Lambert 1ca834900e feat: site WIP — downloads, explorer, hasher, TestFlight enroll
Commit restored mcp.verae.ai site pages (app, register, macos, windows,
explorer), hasher/download artifacts, index/sample/hasher updates, ledger
and global_client improvements, NATS stamp helper, and TestFlight enroll
service. Ignore debug-trace/ and editor backups. Standard Readme for
download and enroll docs.
2026-09-29 11:18:59 -04:00

146 lines
5.9 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Verae WASM hasher</title>
<meta name="description" content="Hash a file in the browser with Verae WASM. Bytes never leave the device.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="icon" href="assets/VeraeFullLogo.png">
<style>
:root {
--ink: oklch(0.21 0.020 310);
--ink-soft: oklch(0.42 0.018 310);
--ink-mute: oklch(0.60 0.014 310);
--paper: oklch(0.992 0.004 90);
--soft: oklch(0.972 0.008 85);
--hair: oklch(0.90 0.010 80);
--purple: oklch(0.32 0.110 310);
--purple-mid: oklch(0.42 0.130 312);
--purple-wash: oklch(0.96 0.020 312);
--gold: oklch(0.80 0.090 85);
--dark: oklch(0.19 0.022 312);
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;
color: var(--ink);
background: var(--paper);
line-height: 1.55;
}
a { color: var(--purple); }
header {
display: flex; align-items: center; justify-content: space-between;
max-width: 1100px; margin: 0 auto; padding: 28px 28px 0; flex-wrap: wrap; gap: 12px;
}
.brand img { height: 46px; width: auto; display: block; }
nav { display: flex; gap: 22px; font-size: 14px; font-weight: 600; flex-wrap: wrap; }
nav a { color: var(--ink-soft); text-decoration: none; }
nav a:hover, nav a.active { color: var(--purple); }
.wrap { max-width: 760px; margin: 0 auto; padding: 36px 28px 80px; }
.eyebrow {
font-size: 13px; font-weight: 700; letter-spacing: 0.14em;
text-transform: uppercase; color: var(--purple-mid); margin: 0 0 8px;
}
h1 {
font-family: Fraunces, Georgia, serif; font-weight: 560;
font-size: clamp(28px, 4vw, 40px); line-height: 1.1;
letter-spacing: -0.03em; margin: 0 0 12px;
}
.lede { color: var(--ink-soft); margin: 0 0 18px; font-size: 16px; }
label { display: block; margin-top: 0.8rem; font-size: 13px; font-weight: 600; color: var(--ink-soft); }
input, select, button, textarea {
font: inherit; width: 100%; box-sizing: border-box; margin-top: 6px;
border: 1px solid var(--hair); border-radius: 10px; padding: 10px 12px; background: var(--soft); color: var(--ink);
}
button {
width: auto; margin-top: 1rem; background: var(--purple); color: white; border: 0;
border-radius: 999px; padding: 11px 18px; font-weight: 700; cursor: pointer;
}
pre {
white-space: pre-wrap; background: var(--dark); color: oklch(0.95 0.01 90);
padding: 0.9rem 1rem; border-radius: 14px; overflow: auto;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px;
}
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em; }
footer {
max-width: 1100px; margin: 0 auto; padding: 0 28px 48px;
display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
font-size: 13px; color: var(--ink-mute);
}
@media (max-width: 800px) { nav { gap: 14px; } }
input[type=file] { display: block; margin: 1rem 0; width: auto; background: transparent; border: 0; padding: 0; }
pre { word-break: break-all; }
</style>
</head>
<body>
<header>
<a class="brand" href="https://www.verae.com/"><img src="assets/VeraeFullLogo.png" alt="Verae"></a>
<nav>
<a href="index.html">Install</a>
<a href="ways.html">Ways to use</a>
<a href="explorer.html">Explorer</a>
<a href="macos.html">macOS</a>
<a href="windows.html">Windows</a>
<a href="app.html">Browser app</a>
<a href="register.html">Register docs</a>
<a href="sample.html">Sample</a>
<a href="hasher.html" class="active">Hasher</a>
</nav>
</header>
<main class="wrap">
<p class="eyebrow">Local WASM · digests only</p>
<h1>Local WASM hasher</h1>
<p class="lede">Hash a file in this browser. Bytes never leave the device. Paste the digest into <a href="sample.html">sample.html</a>, the <a href="app.html">browser app</a>, or your MCP client. More options: <a href="ways.html">Ways to use</a>.</p>
<input type="file" id="f">
<pre id="out">Waiting for a file…</pre>
</main>
<footer>
<span>Verae timestamping · <a href="https://www.verae.com/">verae.com</a></span>
<span><a href="ways.html">Ways to use</a> · <a href="index.html#hasher">Native hasher</a> · <a href="sample.html">Sample</a></span>
</footer>
<script type="module">
const out = document.getElementById("out");
const bases = [
(location.origin || "") + "/hasher",
"https://mcp.georgelambert.org/hasher"
];
async function loadLatest() {
for (const b of bases) {
try {
const latest = await (await fetch(b + "/latest.json", {cache:"no-cache"})).json();
const manUrl = latest.manifest || (b + "/" + latest.version + "/manifest.json");
const man = await (await fetch(manUrl, {cache:"no-cache"})).json();
return man;
} catch (e) {}
}
throw new Error("cannot load hasher manifest");
}
const man = await loadLatest();
const base = man.base_url.replace(/\/$/, "");
const jsUrl = base + "/" + man.wasm.js;
out.textContent = "Loading " + jsUrl + " …";
const mod = await import(jsUrl);
await mod.default();
out.textContent = "Ready. Pick a file.";
document.getElementById("f").addEventListener("change", async (ev) => {
const file = ev.target.files && ev.target.files[0];
if (!file) return;
out.textContent = "Hashing " + file.name + " (" + file.size + " bytes)…";
const stream = new mod.Sha256Stream();
const reader = file.stream().getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
stream.update(value);
}
const digest = stream.finish();
out.textContent = digest + " " + file.name + "\n\nPaste into sample.html or stamp via MCP.";
});
</script>
</body>
</html>