PrintToVeraeTimeStamp/site/printit.verae.ai/certificate.html
George Lambert 5cfb92c82a docs+site: CERT-01 web certificate viewer and packaging links
- certificate.html brand kit preview with multihash when present
- PACKAGING.md CERT section residual
2026-10-13 21:50:36 -04:00

413 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Verae Timestamp Certificate · CERT-01 · PrintIt</title>
<meta name="description" content="CERT-01 branded timestamp certificate viewer for PrintIt. Digests only — final receipt is evidence."/>
<link rel="canonical" href="https://printit.verae.ai/certificate.html"/>
<link rel="stylesheet" href="brand/tokens.css"/>
<link rel="stylesheet" href="verae-ui-kit.css"/>
<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,600;9..144,700&amp;family=JetBrains+Mono:wght@500&amp;display=swap" rel="stylesheet"/>
<style>
body{margin:0;background:var(--canvas,#E9E6EE);color:var(--ink,#1D1534);font-family:var(--font-sans),system-ui,-apple-system,Segoe UI,sans-serif}
.toolbar{padding:12px 20px;background:var(--card,#fff);border-bottom:1px solid var(--line,#E3DEF0);display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.toolbar b{color:var(--plum,#531B59)}
.toolbar .help{font-size:12px;color:var(--muted,#5E5470)}
.toolbar .spacer{flex:1}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;font-weight:600;font-size:13px;border-radius:8px;padding:0 14px;height:36px;border:1px solid transparent;text-decoration:none;cursor:pointer;font-family:inherit}
.btn.secondary{background:var(--card,#fff);color:var(--plum,#531B59);border-color:#CDBFD4}
.btn.primary{background:var(--plum,#531B59);color:#fff}
.btn.tonal{background:var(--plum-100,#EBD9EE);color:var(--plum-d,#3E1343);border:0}
.wrap{max-width:900px;margin:0 auto;padding:24px 16px 48px}
/* CERT-01 paper sheet — plum outer + gold inner frame */
.sheet{
background:#fff;position:relative;border-radius:4px;
box-shadow:0 12px 40px rgba(30,10,40,.18),0 2px 8px rgba(30,10,40,.08);
padding:48px 56px 56px;min-height:720px;
box-shadow:
0 12px 40px rgba(30,10,40,.18),
inset 0 0 0 1.5px var(--plum,#531B59),
inset 0 0 0 6px #fff,
inset 0 0 0 6.5px var(--gold,#C4A85A);
}
.head{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}
.logo-row{display:flex;align-items:center;gap:10px}
.logo-row img{height:36px;width:auto;display:block}
.mark{width:36px;height:36px;border-radius:9px;background:linear-gradient(140deg,var(--plum,#531B59),var(--violet,#6B3FD6));display:grid;place-items:center;color:#fff;font-weight:800;font-size:13px;flex:none}
.cert-meta{text-align:right;font-size:11px;color:var(--muted,#5E5470);line-height:1.45}
.title{
text-align:center;margin:28px 0 0;
font-family:var(--font-serif),Fraunces,Georgia,serif;
font-weight:700;font-size:clamp(22px,3.2vw,26px);line-height:1.25;letter-spacing:-0.01em;color:var(--ink,#1D1534);
}
.subtitle{text-align:center;font-size:12.5px;color:var(--muted,#5E5470);margin:8px auto 0;max-width:34rem;line-height:1.45}
.rule{height:2px;background:var(--gold,#C4A85A);width:120px;margin:14px auto 0;border:0}
.banner{
margin-top:20px;border:2px solid var(--ok,#0E6B3A);background:var(--ok-bg,#E3F5EA);
border-radius:10px;padding:12px 16px;display:flex;gap:12px;align-items:center;
}
.banner.provisional{border-color:var(--pend-bd,#D5DAE1);background:var(--pend-bg,#EEF0F3)}
.banner .check{font-size:28px;line-height:1;color:var(--ok,#0E6B3A);font-weight:700}
.banner.provisional .check{color:var(--pend,#475467)}
.banner .bt{font:700 15px/1.3 var(--font-sans),system-ui;color:var(--ok,#0E6B3A)}
.banner.provisional .bt{color:var(--pend,#475467)}
.banner .bs{font-size:12px;color:#1D4D33;margin-top:2px}
.banner.provisional .bs{color:var(--muted,#5E5470)}
.kv{display:grid;grid-template-columns:150px 1fr;gap:10px 16px;margin-top:22px;font-size:13px}
.kv dt{color:var(--muted,#5E5470);font-weight:600;font-size:12px;padding-top:2px}
.kv dd{margin:0;word-break:break-all;line-height:1.45}
.mono{font-family:var(--font-mono),'JetBrains Mono',ui-monospace,SF Mono,Menlo,monospace;font-size:11px;letter-spacing:-0.01em}
.mh{display:inline-flex;align-items:flex-start;gap:4px;flex-wrap:wrap;background:var(--surface-2,#F4F1F8);border:1px solid var(--line,#E3DEF0);border-radius:6px;padding:4px 8px}
.mh .pfx{color:var(--plum,#531B59);font-weight:600}
.verify-box{
display:flex;gap:18px;align-items:flex-start;margin-top:24px;padding:16px;
border:1px solid var(--line,#E3DEF0);border-radius:10px;background:var(--bg,#FBFAFD);
}
.qr{
width:112px;height:112px;flex:none;border:2px solid var(--ink,#1D1534);border-radius:8px;
background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 29'%3E%3Cpath fill='%231D1534' d='M1 1h7v7H1zm2 2v3h3V3zm10 0h7v7h-7zm2 2v3h3V3zM1 13h7v7H1zm2 2v3h3v-3zm18-14h7v7h-7zm2 2v3h3V3zm0 10h3v2h-3zm-4 0h2v2h-2zm4 4h3v3h-3zm-4 2h2v3h-2zm-2-4h2v2h-2zm6 6h3v2h-3zm-8 0h2v2h-2zm4-2h2v4h-2zm-6 0h2v2h-2z'/%3E%3C/svg%3E") center/72% no-repeat;
position:relative;
}
.qr span{position:absolute;inset:auto;left:50%;top:50%;transform:translate(-50%,-50%);background:#fff;padding:2px 5px;font:700 10px system-ui;color:var(--plum,#531B59)}
.verify-box ol{margin:6px 0 0 16px;padding:0;line-height:1.6;color:var(--ink-soft,#3B3250);font-size:12.5px}
.verify-box a{color:var(--info,#3B2A8C);font-size:10px;word-break:break-all}
.foot{
margin-top:28px;padding-top:14px;border-top:1px solid var(--line,#E3DEF0);
font-size:10px;color:var(--muted,#5E5470);line-height:1.5;
}
.load{margin-top:18px;padding:14px 16px;background:var(--card,#fff);border:1px solid var(--line,#E3DEF0);border-radius:12px}
.load label{display:block;font-size:12px;font-weight:600;color:var(--muted,#5E5470);margin-bottom:6px}
.load input[type=file]{font-size:13px}
.note{font-size:12px;color:var(--muted,#5E5470);margin:8px 0 0;line-height:1.4}
.hidden{display:none!important}
@media print{
.toolbar,.load,.no-print{display:none!important}
html,body{background:#fff!important;margin:0;padding:0}
.wrap{padding:0;max-width:none}
.sheet{
box-shadow:none!important;
border:2px solid #531B59;
outline:1px solid #C4A85A;outline-offset:-5px;
margin:0;border-radius:0;min-height:0;padding:28px 36px 36px;
-webkit-print-color-adjust:exact;print-color-adjust:exact;
}
.rule,.banner,.mh,.qr{ -webkit-print-color-adjust:exact;print-color-adjust:exact }
.rule{background:#C4A85A!important}
a{color:inherit;text-decoration:none}
}
@page{size:letter;margin:12mm}
</style>
</head>
<body>
<div class="toolbar no-print">
<b>CERT-01</b>
<span class="help">PrintIt · Verae Timestamp Certificate (web preview)</span>
<span class="spacer"></span>
<button class="btn secondary" type="button" id="btn-sample">Sample data</button>
<button class="btn primary" type="button" onclick="window.print()">Print</button>
<a class="btn secondary" href="index.html">printit.verae.ai</a>
<a class="btn secondary" href="queue.html">Queue</a>
</div>
<div class="wrap">
<article class="sheet" id="cert" aria-label="Verae Timestamp Certificate">
<div class="head">
<div class="logo-row">
<img src="brand/logo/verae-mark.svg" alt="Verae" width="36" height="36"
onerror="this.style.display='none';this.nextElementSibling.style.display='grid'"/>
<span class="mark" style="display:none" aria-hidden="true">V</span>
<span style="font-weight:700;font-size:14px;color:var(--plum,#531B59)">Verae</span>
</div>
<div class="cert-meta">
<div id="cert-no">Certificate preview</div>
<div>verify.verae.ai · explorer.verae.ai/time</div>
</div>
</div>
<h1 class="title">Verae Timestamp Certificate</h1>
<p class="subtitle">Proof that this exact document existed at the time below, recorded on the Verae Substrate blockchain</p>
<hr class="rule"/>
<div class="banner" id="banner" role="status">
<div class="check" aria-hidden="true">✓</div>
<div>
<div class="bt" id="banner-title">FINAL — Substrate block finalized</div>
<div class="bs" id="banner-sub">GRANDPA-finalized on VeraeNet · digests only</div>
</div>
</div>
<dl class="kv" id="fields">
<dt>Document</dt>
<dd id="f-doc">—</dd>
<dt id="l-mh">Content multihash</dt>
<dd id="f-mh"><span class="mono">—</span></dd>
<dt>SHA-256 (hex)</dt>
<dd id="f-sha"><span class="mono">—</span></dd>
<dt id="l-b3" class="hidden">BLAKE3 (hex)</dt>
<dd id="f-b3" class="hidden">—</dd>
<dt id="l-b3mh" class="hidden">BLAKE3 multihash</dt>
<dd id="f-b3mh" class="hidden">—</dd>
<dt>Server time</dt>
<dd id="f-time">—</dd>
<dt>VeraeTime receipt</dt>
<dd id="f-receipt"><span class="mono">—</span></dd>
<dt>Ledger / merkle</dt>
<dd id="f-merkle">—</dd>
<dt>Issued by</dt>
<dd id="f-issued">Verae Print Stamp (printit)</dd>
</dl>
<div class="verify-box">
<div class="qr" aria-hidden="true"><span>Verae</span></div>
<div>
<b style="font-size:12.5px">Verify this certificate</b>
<ol>
<li>Open the explorer link (or scan the QR on a printed PDF).</li>
<li>Compare the local file’s SHA-256 to the multihash below — file never uploaded.</li>
<li>Only a <b>final</b> / already-registered receipt is evidence. Hold ids are not proof.</li>
</ol>
<div style="margin-top:8px"><a id="verify-link" href="https://explorer.verae.ai/time" target="_blank" rel="noopener">https://explorer.verae.ai/time</a></div>
</div>
</div>
<p class="foot" id="footer-note">
Only the document’s SHA-256 digest was submitted; the file was not uploaded.
Hashes are multihash (sha2-256, code 0x12, length 0x20 → <span class="mono">f1220</span>…).
Hold / pending / jobId values are never evidence.
Verae LLC · verae.ai · explorer.verae.ai
</p>
</article>
<div class="load no-print">
<label for="sidecar">Load a PrintIt <code>.verae.json</code> sidecar (optional)</label>
<input type="file" id="sidecar" accept=".json,application/json"/>
<p class="note">
Query params also work:
<span class="mono">?document=&amp;sha256=&amp;receipt=&amp;created_at=&amp;merkle=&amp;blake3=&amp;block=&amp;helper_version=</span>
Multihash / BLAKE3 lines appear <b>only when data is present</b> — never invented.
Full CERT-01.html Fraunces pixel match is still a design residual; this page uses brand tokens + plum/gold frame.
</p>
</div>
</div>
<script>
(function () {
var SAMPLE = {
document_name: 'Purchase-Order-7781.pdf',
sha256: '20515740e1051f69c552b83ff1a919398feca768090930a4a502f9335273f279',
multihash: 'f122020515740e1051f69c552b83ff1a919398feca768090930a4a502f9335273f279',
receipt_id: 'bafkreihq3examplev7xq',
created_at: '2026-10-09T05:20:28Z',
merkle: 'Block #1,284,530 · extrinsic 1284530-1',
block: '1284530',
helper_version: '0.3.6',
status: 'final',
byte_length: 184220
};
function $(id) { return document.getElementById(id); }
function isHex64(s) {
return typeof s === 'string' && /^[0-9a-fA-F]{64}$/.test(s.replace(/^0x/i, ''));
}
function cleanHex(s) {
if (!s) return '';
s = String(s).trim().replace(/^0x/i, '').toLowerCase();
return isHex64(s) ? s : '';
}
function contentMultihash(sha) {
var h = cleanHex(sha);
return h ? 'f1220' + h : '';
}
function blake3Multihash(b3) {
var h = cleanHex(b3);
return h ? 'f1e20' + h : '';
}
function fmtBytes(n) {
if (n == null || n === '' || isNaN(Number(n))) return '';
n = Number(n);
return n.toLocaleString('en-US') + ' bytes';
}
function fmtBlock(n) {
if (n == null || n === '') return '';
var s = String(n).replace(/,/g, '');
if (!/^\d+$/.test(s)) return String(n);
return Number(s).toLocaleString('en-US');
}
function setRow(labelId, valueId, show, html) {
var l = labelId ? $(labelId) : null;
var v = $(valueId);
if (!v) return;
if (show) {
if (l) l.classList.remove('hidden');
v.classList.remove('hidden');
v.innerHTML = html;
} else {
if (l) l.classList.add('hidden');
v.classList.add('hidden');
v.textContent = '';
}
}
function mhSpan(full) {
if (!full) return '—';
var pfx = full.slice(0, 5);
var rest = full.slice(5);
return '<span class="mh mono"><span class="pfx">' + escapeHtml(pfx) + '</span> ' + escapeHtml(rest) + '</span>';
}
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, function (c) {
return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c];
});
}
function lookupURL(sha, receipt) {
var u = 'https://explorer.verae.ai/time';
var sep = '?';
if (receipt) { u += sep + 'receipt=' + encodeURIComponent(receipt); sep = '&'; }
if (sha) { u += sep + 'sha256=' + encodeURIComponent(sha); }
return u;
}
function apply(data) {
data = data || {};
var doc = data.document_name || data.document || data.file || '';
var sha = cleanHex(data.sha256 || data.hash || '');
var mh = (data.multihash || data.content_multihash || '').trim() || contentMultihash(sha);
if (mh && !/^f1220/i.test(mh) && isHex64(mh)) mh = 'f1220' + mh.toLowerCase();
var b3 = cleanHex(data.blake3 || '');
var b3mh = (data.blake3_multihash || '').trim() || blake3Multihash(b3);
var receipt = (data.receipt_id || data.receipt || data.certificate || '').trim();
var created = (data.created_at || data.timestamp || data.server_time || '').trim();
var merkle = (data.merkle || data.ledger || '').trim();
var block = data.block || data.block_number || data.blockNumber || '';
var helper = data.helper_version || data.helperVersion || '';
var bytes = data.byte_length || data.bytes || data.size_bytes;
var status = (data.status || data.state || 'final').toLowerCase();
var evidence = status === 'final' || status === 'existing' || status === 'completed' || status === 'ok';
var docLine = doc || '(digest only)';
if (bytes) docLine += ' · ' + fmtBytes(bytes);
$('f-doc').textContent = docLine;
setRow('l-mh', 'f-mh', !!mh, mhSpan(mh));
$('f-sha').innerHTML = sha
? '<span class="mono">' + escapeHtml(sha) + '</span>'
: '<span class="mono">—</span>';
setRow('l-b3', 'f-b3', !!b3, '<span class="mono">' + escapeHtml(b3) + '</span>');
setRow('l-b3mh', 'f-b3mh', !!b3mh, mhSpan(b3mh));
if (created) {
$('f-time').textContent = created + ' (VeraeTime) — never the local clock';
} else {
$('f-time').textContent = '— (server time only; never invent local clock)';
}
$('f-receipt').innerHTML = receipt
? '<span class="mono">' + escapeHtml(receipt) + '</span>'
: '<span class="mono">—</span>';
var merkleLine = merkle || (block ? ('Block #' + fmtBlock(block)) : '—');
$('f-merkle').textContent = merkleLine;
var issued = 'Verae Print Stamp';
if (helper) issued += ' ' + helper;
issued += ' (printit)';
$('f-issued').textContent = issued;
var banner = $('banner');
if (evidence) {
banner.classList.remove('provisional');
var blk = fmtBlock(block);
$('banner-title').textContent = blk
? ('FINAL — Substrate block #' + blk + ', finalized')
: (status === 'existing' ? 'Already registered — final evidence' : 'FINAL — Substrate block finalized');
$('banner-sub').textContent = 'GRANDPA-finalized on VeraeNet (rpc.verae.ai)' +
(created ? (' · ' + created) : '');
} else {
banner.classList.add('provisional');
$('banner-title').textContent = 'Not a certificate yet — pending finality';
$('banner-sub').textContent = 'Hold / pending / included states are not evidence. statuslang: only final & already registered.';
}
$('cert-no').textContent = receipt
? ('Receipt ' + (receipt.length > 28 ? receipt.slice(0, 12) + '…' + receipt.slice(-8) : receipt))
: 'Certificate preview';
var url = lookupURL(sha, receipt);
var a = $('verify-link');
a.href = url;
a.textContent = url;
}
function fromQuery() {
var q = new URLSearchParams(location.search);
if (![...q.keys()].length) return null;
return {
document_name: q.get('document') || q.get('file') || '',
sha256: q.get('sha256') || q.get('hash') || '',
multihash: q.get('multihash') || '',
blake3: q.get('blake3') || '',
blake3_multihash: q.get('blake3_multihash') || '',
receipt_id: q.get('receipt') || q.get('receipt_id') || q.get('certificate') || '',
created_at: q.get('created_at') || q.get('timestamp') || '',
merkle: q.get('merkle') || '',
block: q.get('block') || q.get('block_number') || '',
helper_version: q.get('helper_version') || '',
byte_length: q.get('bytes') || q.get('byte_length') || '',
status: q.get('status') || 'final'
};
}
function normalizeSidecar(j) {
return {
document_name: j.document_name || j.document || j.file || j.original_file || '',
sha256: j.sha256 || j.hash || '',
multihash: j.multihash || j.content_multihash || '',
blake3: j.blake3 || '',
blake3_multihash: j.blake3_multihash || '',
receipt_id: j.receipt_id || j.receipt || j.certificate || '',
created_at: j.created_at || j.timestamp || '',
merkle: j.merkle || '',
block: j.block_number || j.block || (j.chain && j.chain.block) || '',
helper_version: j.helper_version || '',
byte_length: j.byte_length || j.size_bytes || '',
status: j.status || 'final'
};
}
var q = fromQuery();
apply(q || SAMPLE);
$('btn-sample').addEventListener('click', function () {
history.replaceState(null, '', location.pathname);
apply(SAMPLE);
});
$('sidecar').addEventListener('change', function (ev) {
var f = ev.target.files && ev.target.files[0];
if (!f) return;
var r = new FileReader();
r.onload = function () {
try {
var j = JSON.parse(r.result);
apply(normalizeSidecar(j));
} catch (e) {
alert('Could not parse JSON sidecar: ' + e.message);
}
};
r.readAsText(f);
});
})();
</script>
</body>
</html>