@rel-time/mcp-server 1.2.8 → 1.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/index.js +1450 -320
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -56,7 +56,7 @@ let LAST_INVOICE_PACK = null;
|
|
|
56
56
|
|
|
57
57
|
const SERVER_INFO = {
|
|
58
58
|
name: "rel-time-mcp-server",
|
|
59
|
-
version: "1.2.
|
|
59
|
+
version: "1.2.10",
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -135,13 +135,13 @@ function buildPackSelectionPrompt(contextMessage) {
|
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
pack: "launch",
|
|
138
|
-
name: "2. Agent Launch Pack ($5 /
|
|
138
|
+
name: "2. Agent Launch Pack ($5 / 25 Hashes)",
|
|
139
139
|
price_usd: "$5.00",
|
|
140
|
-
credits:
|
|
140
|
+
credits: 25,
|
|
141
141
|
settlement_queue: "Priority 10-Minute Bitcoin Queue",
|
|
142
|
-
hashing_modes: "Standard Mode only (Data Integrity + Biometric or Anonymous)",
|
|
142
|
+
hashing_modes: "Standard Mode only (Data Integrity + Biometric or Anonymous · Max 3 packs per key)",
|
|
143
143
|
payment_methods: ["lightning", "stripe"],
|
|
144
|
-
outputs: "64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + .JSON Doomsday Proof",
|
|
144
|
+
outputs: "64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + 4-Plate Certificate + .JSON Doomsday Proof",
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
147
|
pack: "standard",
|
|
@@ -152,21 +152,21 @@ function buildPackSelectionPrompt(contextMessage) {
|
|
|
152
152
|
hashing_modes: "Temporal Mode (T_index sub-second VDF sequencing) + Standard Mode (Biometric or Anonymous)",
|
|
153
153
|
payment_methods: ["lightning", "stripe"],
|
|
154
154
|
outputs:
|
|
155
|
-
"64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + .JSON Doomsday Proof + Reloadable anytime (+500 credits / $50)",
|
|
155
|
+
"64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + 4-Plate Certificate + .JSON Doomsday Proof + Reloadable anytime (+500 credits / $50)",
|
|
156
156
|
},
|
|
157
157
|
{
|
|
158
158
|
pack: "enterprise",
|
|
159
|
-
name: "4. Rel-Time Superhighway ($
|
|
160
|
-
price_usd: "$
|
|
159
|
+
name: "4. Rel-Time Superhighway ($399 / 5,000 Hashes)",
|
|
160
|
+
price_usd: "$399.00",
|
|
161
161
|
credits: 5000,
|
|
162
162
|
settlement_queue: "Priority 10-Minute Bitcoin Queue",
|
|
163
|
-
hashing_modes: "Temporal Mode (T_index) + Standard Mode +
|
|
163
|
+
hashing_modes: "Temporal Mode (T_index) + Standard Mode + Data Integrity Audit & Instant Tamper Detection",
|
|
164
164
|
payment_methods: ["lightning", "stripe"],
|
|
165
|
-
outputs: "64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + Certificate
|
|
165
|
+
outputs: "64-Ray Sun-Stamp SVG + 1-Click Stamped Copy (.PDF/.PNG) + 4-Plate Certificate + Forensic Audit Report + .JSON Proof",
|
|
166
166
|
},
|
|
167
167
|
],
|
|
168
168
|
next_step:
|
|
169
|
-
"Ask the user which tier (`universal` $3, `launch` $5/
|
|
169
|
+
"Ask the user which tier (`universal` $3, `launch` $5/25, `standard` $50/500, or `enterprise` $399/5,000) and which payment method (`lightning` or `stripe`) they prefer, then call `reltime_provision_agent_key({ action: 'create_invoice', pack, payment_method })`.",
|
|
170
170
|
};
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -506,7 +506,7 @@ function escapeHtml(str) {
|
|
|
506
506
|
}
|
|
507
507
|
|
|
508
508
|
/**
|
|
509
|
-
* Generates a self-contained,
|
|
509
|
+
* Generates a self-contained, institutional-grade Rel-Time Superhighway Cryptographic Audit Trail HTML report.
|
|
510
510
|
*/
|
|
511
511
|
function generateAuditReportHtml(audit) {
|
|
512
512
|
const generatedUtc = audit.generated_at_utc || new Date().toISOString();
|
|
@@ -518,99 +518,196 @@ function generateAuditReportHtml(audit) {
|
|
|
518
518
|
const localCheck = audit.local_path_verification || null;
|
|
519
519
|
const entries = Array.isArray(audit.audit_trail) ? audit.audit_trail : [];
|
|
520
520
|
|
|
521
|
+
const totalMatched = Math.max(1, Number(sum.matched_entries_count || entries.length || 1));
|
|
522
|
+
const settledCount = Number(sum.settled_on_bitcoin_count || 0);
|
|
523
|
+
const pendingCount = Number(sum.pending_finality_count || 0);
|
|
524
|
+
const standardCount = Number(modes.standard_count || 0);
|
|
525
|
+
const temporalCount = Number(modes.temporal_vdf_count || 0);
|
|
526
|
+
const biometricCount = Number(modes.biometric_passkey_count || 0);
|
|
527
|
+
const machineCount = Math.max(0, Number(sum.matched_entries_count || entries.length) - biometricCount);
|
|
528
|
+
|
|
529
|
+
const settledPct = Math.round((settledCount / totalMatched) * 100);
|
|
530
|
+
const standardPct = Math.round((standardCount / totalMatched) * 100);
|
|
531
|
+
const biometricPct = Math.round((biometricCount / totalMatched) * 100);
|
|
532
|
+
|
|
533
|
+
// Build Bitcoin Block Settlement Topology nodes from entries
|
|
534
|
+
const blockGroupsMap = new Map();
|
|
535
|
+
for (const e of entries) {
|
|
536
|
+
if (e.status === "settled" && e.block_height) {
|
|
537
|
+
const bh = String(e.block_height);
|
|
538
|
+
const cur = blockGroupsMap.get(bh) || {
|
|
539
|
+
block_height: bh,
|
|
540
|
+
count: 0,
|
|
541
|
+
txid: e.txid || null,
|
|
542
|
+
max_ticks: 0,
|
|
543
|
+
timestamp_utc: e.timestamp_utc || null,
|
|
544
|
+
};
|
|
545
|
+
cur.count++;
|
|
546
|
+
if (!cur.txid && e.txid) cur.txid = e.txid;
|
|
547
|
+
if (Number(e.vdf_ticks || 0) > cur.max_ticks) cur.max_ticks = Number(e.vdf_ticks || 0);
|
|
548
|
+
blockGroupsMap.set(bh, cur);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
const sortedBlocks = Array.from(blockGroupsMap.values())
|
|
552
|
+
.sort((a, b) => Number(a.block_height) - Number(b.block_height))
|
|
553
|
+
.slice(-5);
|
|
554
|
+
|
|
555
|
+
const topologyNodesHtml = [
|
|
556
|
+
...sortedBlocks.map(
|
|
557
|
+
(b) => `
|
|
558
|
+
<div class="topo-node">
|
|
559
|
+
<div class="topo-node-top">
|
|
560
|
+
<span class="dot dot-settled"></span>
|
|
561
|
+
<span class="mono topo-height">BLOCK #${escapeHtml(b.block_height)}</span>
|
|
562
|
+
</div>
|
|
563
|
+
<div class="topo-metric mono">${b.count} ${b.count === 1 ? "ANCHOR" : "ANCHORS"}</div>
|
|
564
|
+
<div class="topo-meta mono">
|
|
565
|
+
${
|
|
566
|
+
b.txid
|
|
567
|
+
? `<a href="https://mempool.space/tx/${escapeHtml(b.txid)}" target="_blank" rel="noopener">TX ${escapeHtml(
|
|
568
|
+
b.txid.slice(0, 8)
|
|
569
|
+
)}…${escapeHtml(b.txid.slice(-4))} ↗</a>`
|
|
570
|
+
: "SETTLED"
|
|
571
|
+
}
|
|
572
|
+
</div>
|
|
573
|
+
</div>`
|
|
574
|
+
),
|
|
575
|
+
pendingCount > 0
|
|
576
|
+
? `
|
|
577
|
+
<div class="topo-node topo-node-pending">
|
|
578
|
+
<div class="topo-node-top">
|
|
579
|
+
<span class="dot dot-pending"></span>
|
|
580
|
+
<span class="mono topo-height">VDF MEMPOOL QUEUE</span>
|
|
581
|
+
</div>
|
|
582
|
+
<div class="topo-metric mono">${pendingCount} PENDING</div>
|
|
583
|
+
<div class="topo-meta mono">NEXT EPOCH (~10m)</div>
|
|
584
|
+
</div>`
|
|
585
|
+
: "",
|
|
586
|
+
]
|
|
587
|
+
.filter(Boolean)
|
|
588
|
+
.join(`<div class="topo-connector"></div>`);
|
|
589
|
+
|
|
521
590
|
const signerRowsHtml = signers.length
|
|
522
591
|
? signers
|
|
523
|
-
.map(
|
|
524
|
-
(s
|
|
592
|
+
.map((s, idx) => {
|
|
593
|
+
const sharePct = Math.round((Number(s.anchor_count || 0) / totalMatched) * 100);
|
|
594
|
+
return `
|
|
525
595
|
<tr>
|
|
526
|
-
<td class="mono"
|
|
596
|
+
<td class="mono muted">${String(idx + 1).padStart(2, "0")}</td>
|
|
527
597
|
<td>
|
|
528
|
-
<
|
|
529
|
-
|
|
598
|
+
<div class="signer-key-header">
|
|
599
|
+
<span class="tag tag-neutral">FIDO2 / COSE P-256</span>
|
|
600
|
+
<span class="mono muted">KEY ID: ${escapeHtml(s.signer_public_key.slice(0, 16).toUpperCase())}</span>
|
|
601
|
+
</div>
|
|
602
|
+
<div class="mono hash-full" style="margin-top:5px">${escapeHtml(s.signer_public_key)}</div>
|
|
530
603
|
</td>
|
|
531
|
-
<td class="mono"
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
604
|
+
<td class="mono">
|
|
605
|
+
<div style="display:flex;align-items:center;gap:10px">
|
|
606
|
+
<span style="font-weight:700;color:var(--text);min-width:24px">${s.anchor_count}</span>
|
|
607
|
+
<div class="mini-bar-track"><div class="mini-bar-fill" style="width:${sharePct}%"></div></div>
|
|
608
|
+
<span class="muted" style="font-size:11px">${sharePct}%</span>
|
|
609
|
+
</div>
|
|
610
|
+
</td>
|
|
611
|
+
<td class="mono">${s.settled_count} / ${s.anchor_count}</td>
|
|
612
|
+
<td class="mono muted">${escapeHtml(s.latest_timestamp_utc || "—")}</td>
|
|
613
|
+
</tr>`;
|
|
614
|
+
})
|
|
536
615
|
.join("")
|
|
537
|
-
: `<tr><td colspan="5"
|
|
616
|
+
: `<tr><td colspan="5" class="empty-cell">No hardware Passkey signatures recorded in this window. Link a FIDO2/WebAuthn hardware key with <code>reltime_link_passkey</code> and anchor with <code>biometric: true</code>.</td></tr>`;
|
|
538
617
|
|
|
539
618
|
const localVerificationHtml = localCheck
|
|
540
619
|
? `
|
|
541
|
-
<
|
|
542
|
-
<div class="
|
|
620
|
+
<section class="panel">
|
|
621
|
+
<div class="panel-header">
|
|
543
622
|
<div>
|
|
544
|
-
<
|
|
545
|
-
<
|
|
623
|
+
<div class="section-index">SECTION 01 // ZERO-KNOWLEDGE LOCAL FILE INTEGRITY MATRIX</div>
|
|
624
|
+
<h2>Local Directory Cross-Reference</h2>
|
|
625
|
+
<p class="section-sub">Target Path: <span class="mono" style="color:var(--text-secondary)">${escapeHtml(
|
|
626
|
+
localCheck.target_path
|
|
627
|
+
)}</span> · 0 Bytes Uploaded · Local SHA-256 vs. Bitcoin Ledger</p>
|
|
546
628
|
</div>
|
|
547
|
-
<
|
|
548
|
-
localCheck.summary.unanchored_or_modified_count === 0 ? "
|
|
629
|
+
<div class="status-pill ${
|
|
630
|
+
localCheck.summary.unanchored_or_modified_count === 0 ? "status-pill-ok" : "status-pill-alert"
|
|
549
631
|
}">
|
|
550
|
-
${
|
|
632
|
+
<span class="dot ${
|
|
633
|
+
localCheck.summary.unanchored_or_modified_count === 0 ? "dot-settled" : "dot-alert"
|
|
634
|
+
}"></span>
|
|
635
|
+
<span>${
|
|
551
636
|
localCheck.summary.unanchored_or_modified_count === 0
|
|
552
|
-
? `
|
|
553
|
-
: `${localCheck.summary.unanchored_or_modified_count} UNANCHORED
|
|
554
|
-
}
|
|
555
|
-
</
|
|
637
|
+
? `INTEGRITY VERIFIED (${localCheck.summary.total_files_scanned}/${localCheck.summary.total_files_scanned})`
|
|
638
|
+
: `${localCheck.summary.unanchored_or_modified_count} UNANCHORED OR MODIFIED`
|
|
639
|
+
}</span>
|
|
640
|
+
</div>
|
|
556
641
|
</div>
|
|
557
642
|
<div class="table-wrap">
|
|
558
643
|
<table>
|
|
559
644
|
<thead>
|
|
560
645
|
<tr>
|
|
561
|
-
<th
|
|
562
|
-
<th>
|
|
563
|
-
<th>
|
|
564
|
-
<th>
|
|
565
|
-
<th>
|
|
566
|
-
<th>
|
|
567
|
-
<th>
|
|
646
|
+
<th style="width:42px">#</th>
|
|
647
|
+
<th>Artifact / File Name</th>
|
|
648
|
+
<th>Bytes</th>
|
|
649
|
+
<th>Local SHA-256 Digest</th>
|
|
650
|
+
<th>Verification Status</th>
|
|
651
|
+
<th>Attestation / Mode</th>
|
|
652
|
+
<th>Timechain Settlement</th>
|
|
653
|
+
<th style="text-align:right">Receipt</th>
|
|
568
654
|
</tr>
|
|
569
655
|
</thead>
|
|
570
656
|
<tbody>
|
|
571
657
|
${localCheck.files
|
|
572
|
-
.map((f) => {
|
|
573
|
-
const
|
|
658
|
+
.map((f, idx) => {
|
|
659
|
+
const statusTag =
|
|
574
660
|
f.integrity_status === "VERIFIED_SETTLED"
|
|
575
|
-
? `<span class="
|
|
661
|
+
? `<span class="status-cell"><span class="dot dot-settled"></span><span class="mono" style="color:var(--text)">VERIFIED · SETTLED</span></span>`
|
|
576
662
|
: f.integrity_status === "VERIFIED_PENDING"
|
|
577
|
-
? `<span class="
|
|
578
|
-
: `<span class="
|
|
663
|
+
? `<span class="status-cell"><span class="dot dot-pending"></span><span class="mono" style="color:var(--text-secondary)">VERIFIED · PENDING</span></span>`
|
|
664
|
+
: `<span class="status-cell"><span class="dot dot-alert"></span><span class="mono" style="color:var(--crimson)">UNANCHORED / MODIFIED</span></span>`;
|
|
665
|
+
|
|
579
666
|
const signerText = f.signer_public_key
|
|
580
|
-
? `<span class="
|
|
581
|
-
f.signer_public_key
|
|
582
|
-
)}…</span>`
|
|
583
|
-
:
|
|
667
|
+
? `<span class="tag tag-neutral" title="Passkey COSE: ${escapeHtml(
|
|
668
|
+
f.signer_public_key
|
|
669
|
+
)}">PASSKEY ${escapeHtml(f.signer_public_key.slice(0, 8).toUpperCase())}…</span>`
|
|
670
|
+
: f.anchor_mode
|
|
671
|
+
? `<span class="tag tag-neutral">${escapeHtml(f.anchor_mode.toUpperCase())}</span>`
|
|
672
|
+
: `<span class="mono muted">—</span>`;
|
|
673
|
+
|
|
584
674
|
const btcText = f.block_height
|
|
585
|
-
? `<span class="mono" style="color
|
|
675
|
+
? `<span class="mono" style="color:var(--text)">BLOCK #${escapeHtml(f.block_height)}</span>`
|
|
586
676
|
: f.integrity_status === "VERIFIED_PENDING"
|
|
587
|
-
? `<span class="mono
|
|
588
|
-
:
|
|
677
|
+
? `<span class="mono muted">MEMPOOL QUEUE</span>`
|
|
678
|
+
: `<span class="mono muted">—</span>`;
|
|
679
|
+
|
|
589
680
|
const verifyLink = f.verify_url
|
|
590
|
-
? `<a class="
|
|
591
|
-
:
|
|
681
|
+
? `<a class="action-link" href="${escapeHtml(f.verify_url)}" target="_blank" rel="noopener">VERIFY ↗</a>`
|
|
682
|
+
: `<span class="mono muted">—</span>`;
|
|
683
|
+
|
|
592
684
|
return `
|
|
593
685
|
<tr>
|
|
594
|
-
<td
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
686
|
+
<td class="mono muted">${String(idx + 1).padStart(2, "0")}</td>
|
|
687
|
+
<td>
|
|
688
|
+
<span style="font-weight:600;color:var(--text)">${escapeHtml(f.file_name)}</span>
|
|
689
|
+
${f.is_stamped_copy ? `<span class="tag tag-subtle" style="margin-left:8px">STAMPED COPY</span>` : ""}
|
|
690
|
+
</td>
|
|
691
|
+
<td class="mono muted">${escapeHtml(f.size_human)}</td>
|
|
692
|
+
<td class="mono" style="color:var(--text-secondary)" title="${escapeHtml(f.local_sha256)}">${escapeHtml(
|
|
599
693
|
f.local_sha256.slice(0, 16)
|
|
600
|
-
)}…${escapeHtml(f.local_sha256.slice(-
|
|
601
|
-
<td>${
|
|
694
|
+
)}…${escapeHtml(f.local_sha256.slice(-10))}</td>
|
|
695
|
+
<td>${statusTag}</td>
|
|
602
696
|
<td>${signerText}</td>
|
|
603
697
|
<td>${btcText}</td>
|
|
604
|
-
<td>${verifyLink}</td>
|
|
698
|
+
<td style="text-align:right">${verifyLink}</td>
|
|
605
699
|
</tr>`;
|
|
606
700
|
})
|
|
607
701
|
.join("")}
|
|
608
702
|
</tbody>
|
|
609
703
|
</table>
|
|
610
704
|
</div>
|
|
611
|
-
</
|
|
705
|
+
</section>`
|
|
612
706
|
: "";
|
|
613
707
|
|
|
708
|
+
const ledgerSectionNum = localCheck ? "03" : "02";
|
|
709
|
+
const signerSectionNum = localCheck ? "02" : "01";
|
|
710
|
+
|
|
614
711
|
const ledgerRowsHtml = entries.length
|
|
615
712
|
? entries
|
|
616
713
|
.map((e, idx) => {
|
|
@@ -626,34 +723,34 @@ function generateAuditReportHtml(audit) {
|
|
|
626
723
|
.join(" ");
|
|
627
724
|
|
|
628
725
|
const statusHtml = isSettled
|
|
629
|
-
? `<span class="
|
|
630
|
-
: `<span class="
|
|
726
|
+
? `<span class="status-cell"><span class="dot dot-settled"></span><span class="mono" style="color:var(--text)">SETTLED</span></span>`
|
|
727
|
+
: `<span class="status-cell"><span class="dot dot-pending"></span><span class="mono muted">PENDING</span></span>`;
|
|
631
728
|
|
|
632
729
|
const modeHtml = `
|
|
633
|
-
<span class="
|
|
730
|
+
<span class="tag tag-neutral">${escapeHtml(modeType.toUpperCase())}</span>
|
|
634
731
|
${
|
|
635
732
|
isBiometric
|
|
636
|
-
? `<span class="
|
|
733
|
+
? `<span class="tag tag-subtle" title="Passkey COSE: ${escapeHtml(
|
|
637
734
|
e.signer_public_key
|
|
638
|
-
)}"
|
|
735
|
+
)}">PK:${escapeHtml(e.signer_public_key.slice(0, 8).toUpperCase())}</span>`
|
|
639
736
|
: ""
|
|
640
737
|
}
|
|
641
738
|
`;
|
|
642
739
|
|
|
643
740
|
const btcHtml = isSettled
|
|
644
741
|
? `
|
|
645
|
-
<div class="mono" style="color
|
|
742
|
+
<div class="mono" style="color:var(--text);font-weight:600">BLOCK #${escapeHtml(e.block_height || "—")}</div>
|
|
646
743
|
${
|
|
647
744
|
e.txid
|
|
648
745
|
? `<a class="mono tx-link" href="https://mempool.space/tx/${escapeHtml(
|
|
649
746
|
e.txid
|
|
650
|
-
)}" target="_blank" rel="noopener" title="${escapeHtml(e.txid)}">TX
|
|
747
|
+
)}" target="_blank" rel="noopener" title="${escapeHtml(e.txid)}">TX ${escapeHtml(
|
|
651
748
|
e.txid.slice(0, 10)
|
|
652
749
|
)}…${escapeHtml(e.txid.slice(-6))} ↗</a>`
|
|
653
750
|
: ""
|
|
654
751
|
}
|
|
655
752
|
`
|
|
656
|
-
: `<span class="mono
|
|
753
|
+
: `<span class="mono muted">AWAITING BLOCK</span>`;
|
|
657
754
|
|
|
658
755
|
return `
|
|
659
756
|
<tr class="ledger-row ${filterClasses}" data-search="${escapeHtml(
|
|
@@ -661,367 +758,682 @@ function generateAuditReportHtml(audit) {
|
|
|
661
758
|
e.txid || ""
|
|
662
759
|
} ${e.block_height || ""} ${modeType}`.toLowerCase()
|
|
663
760
|
)}">
|
|
664
|
-
<td class="mono
|
|
665
|
-
<td class="mono">${escapeHtml(e.timestamp_utc || "—")}</td>
|
|
761
|
+
<td class="mono muted">${String(idx + 1).padStart(2, "0")}</td>
|
|
762
|
+
<td class="mono muted">${escapeHtml((e.timestamp_utc || "—").replace(".000Z", "Z"))}</td>
|
|
666
763
|
<td>${statusHtml}</td>
|
|
667
764
|
<td>${modeHtml}</td>
|
|
668
|
-
<td class="mono
|
|
765
|
+
<td class="mono" style="color:var(--text-secondary)" title="Client Hash: ${escapeHtml(e.client_hash)}${
|
|
669
766
|
e.original_file_hash ? ` | Original File SHA-256: ${escapeHtml(e.original_file_hash)}` : ""
|
|
670
767
|
}">
|
|
671
|
-
<div>${escapeHtml(e.client_hash.slice(0, 14))}…${escapeHtml(e.client_hash.slice(-
|
|
768
|
+
<div>${escapeHtml(e.client_hash.slice(0, 14))}…${escapeHtml(e.client_hash.slice(-10))}</div>
|
|
672
769
|
${
|
|
673
770
|
e.original_file_hash
|
|
674
|
-
? `<div style="font-size:
|
|
675
|
-
e.original_file_hash.slice(0,
|
|
771
|
+
? `<div class="muted" style="font-size:10.5px;margin-top:2px">FILE: ${escapeHtml(
|
|
772
|
+
e.original_file_hash.slice(0, 12)
|
|
676
773
|
)}…</div>`
|
|
677
774
|
: ""
|
|
678
775
|
}
|
|
679
776
|
</td>
|
|
680
|
-
<td class="mono
|
|
777
|
+
<td class="mono" style="color:var(--text-secondary)" title="T_index: ${escapeHtml(e.t_index)} | Solar Ticks: ${escapeHtml(
|
|
681
778
|
e.vdf_ticks
|
|
682
779
|
)}">
|
|
683
|
-
<div>${escapeHtml(e.t_index.slice(0,
|
|
684
|
-
<div style="font-size:
|
|
780
|
+
<div>${escapeHtml(e.t_index.slice(0, 14))}…${escapeHtml(e.t_index.slice(-8))}</div>
|
|
781
|
+
<div class="muted" style="font-size:10.5px;margin-top:2px">VDF TICKS: ${Number(
|
|
782
|
+
e.vdf_ticks || 0
|
|
783
|
+
).toLocaleString()}</div>
|
|
685
784
|
</td>
|
|
686
785
|
<td>${btcHtml}</td>
|
|
687
|
-
<td>
|
|
688
|
-
<a class="
|
|
786
|
+
<td style="text-align:right">
|
|
787
|
+
<a class="action-link" href="${escapeHtml(e.verify_url)}" target="_blank" rel="noopener">VERIFY ↗</a>
|
|
689
788
|
</td>
|
|
690
789
|
</tr>`;
|
|
691
790
|
})
|
|
692
791
|
.join("")
|
|
693
|
-
: `<tr><td colspan="8"
|
|
792
|
+
: `<tr><td colspan="8" class="empty-cell">No ledger entries matched the active filter criteria.</td></tr>`;
|
|
694
793
|
|
|
695
794
|
return `<!DOCTYPE html>
|
|
696
|
-
<html lang="en">
|
|
795
|
+
<html lang="en" data-theme="dark">
|
|
697
796
|
<head>
|
|
698
797
|
<meta charset="UTF-8" />
|
|
699
798
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
700
|
-
<title>Rel-Time Superhighway
|
|
799
|
+
<title>Rel-Time Superhighway // Forensic Timechain Audit Report</title>
|
|
701
800
|
<style>
|
|
702
801
|
:root {
|
|
703
|
-
--bg: #
|
|
704
|
-
--
|
|
705
|
-
--
|
|
706
|
-
--
|
|
707
|
-
--
|
|
708
|
-
--
|
|
709
|
-
--
|
|
710
|
-
--
|
|
711
|
-
--
|
|
712
|
-
--
|
|
713
|
-
--
|
|
714
|
-
--
|
|
715
|
-
--
|
|
716
|
-
--
|
|
717
|
-
--muted: #
|
|
802
|
+
--bg: #050506;
|
|
803
|
+
--grid-line: rgba(255, 255, 255, 0.015);
|
|
804
|
+
--panel: #0a0a0c;
|
|
805
|
+
--panel-alt: #0e0e12;
|
|
806
|
+
--border: #1e1e24;
|
|
807
|
+
--border-strong: #2e2e38;
|
|
808
|
+
--text: #f4f4f5;
|
|
809
|
+
--text-secondary: #d4d4d8;
|
|
810
|
+
--muted: #71717a;
|
|
811
|
+
--amber: #f59e0b;
|
|
812
|
+
--emerald: #10b981;
|
|
813
|
+
--crimson: #ef4444;
|
|
814
|
+
--track-bg: #18181d;
|
|
815
|
+
--bar-primary: #e4e4e7;
|
|
816
|
+
--bar-muted: #3f3f46;
|
|
817
|
+
--tag-bg: #141418;
|
|
818
|
+
--tab-active-bg: #18181f;
|
|
819
|
+
--row-hover: rgba(255, 255, 255, 0.015);
|
|
820
|
+
--btn-primary-fg: #050506;
|
|
821
|
+
}
|
|
822
|
+
html[data-theme="light"] {
|
|
823
|
+
--bg: #f4f4f6;
|
|
824
|
+
--grid-line: rgba(0, 0, 0, 0.03);
|
|
825
|
+
--panel: #ffffff;
|
|
826
|
+
--panel-alt: #f8f8fa;
|
|
827
|
+
--border: #e4e4e7;
|
|
828
|
+
--border-strong: #d4d4d8;
|
|
829
|
+
--text: #09090b;
|
|
830
|
+
--text-secondary: #3f3f46;
|
|
831
|
+
--muted: #71717a;
|
|
832
|
+
--amber: #d97706;
|
|
833
|
+
--emerald: #059669;
|
|
834
|
+
--crimson: #dc2626;
|
|
835
|
+
--track-bg: #e4e4e7;
|
|
836
|
+
--bar-primary: #18181b;
|
|
837
|
+
--bar-muted: #a1a1aa;
|
|
838
|
+
--tag-bg: #f4f4f5;
|
|
839
|
+
--tab-active-bg: #f4f4f5;
|
|
840
|
+
--row-hover: rgba(0, 0, 0, 0.02);
|
|
841
|
+
--btn-primary-fg: #ffffff;
|
|
718
842
|
}
|
|
719
843
|
* { box-sizing: border-box; }
|
|
720
844
|
body {
|
|
721
845
|
margin: 0;
|
|
722
|
-
padding:
|
|
723
|
-
background:
|
|
846
|
+
padding: 28px 24px 64px;
|
|
847
|
+
background-color: var(--bg);
|
|
848
|
+
background-image:
|
|
849
|
+
linear-gradient(to right, var(--grid-line) 1px, transparent 1px),
|
|
850
|
+
linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px);
|
|
851
|
+
background-size: 32px 32px;
|
|
724
852
|
color: var(--text);
|
|
725
853
|
font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif;
|
|
726
|
-
|
|
854
|
+
font-size: 13px;
|
|
855
|
+
line-height: 1.45;
|
|
856
|
+
-webkit-font-smoothing: antialiased;
|
|
857
|
+
transition: background-color 0.15s ease, color 0.15s ease;
|
|
858
|
+
}
|
|
859
|
+
.container { max-width: 1360px; margin: 0 auto; }
|
|
860
|
+
.mono {
|
|
861
|
+
font-family: "JetBrains Mono", "SF Mono", "Fira Code", Menlo, Consolas, monospace;
|
|
862
|
+
font-variant-numeric: tabular-nums;
|
|
863
|
+
font-size: 12px;
|
|
727
864
|
}
|
|
728
|
-
.
|
|
729
|
-
|
|
865
|
+
.muted { color: var(--muted); }
|
|
866
|
+
|
|
867
|
+
/* Masthead */
|
|
868
|
+
.masthead {
|
|
869
|
+
background: var(--panel);
|
|
870
|
+
border: 1px solid var(--border);
|
|
871
|
+
border-top: 2px solid var(--amber);
|
|
872
|
+
padding: 22px 26px;
|
|
730
873
|
display: flex;
|
|
731
874
|
flex-wrap: wrap;
|
|
732
875
|
justify-content: space-between;
|
|
733
|
-
align-items:
|
|
734
|
-
gap:
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
}
|
|
742
|
-
.brand-eyebrow {
|
|
743
|
-
font-size: 11px;
|
|
744
|
-
letter-spacing: 0.18em;
|
|
876
|
+
align-items: flex-start;
|
|
877
|
+
gap: 20px;
|
|
878
|
+
margin-bottom: 16px;
|
|
879
|
+
}
|
|
880
|
+
.eyebrow {
|
|
881
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
882
|
+
font-size: 10.5px;
|
|
883
|
+
letter-spacing: 0.16em;
|
|
745
884
|
text-transform: uppercase;
|
|
746
|
-
color: var(--
|
|
747
|
-
|
|
748
|
-
margin-bottom: 4px;
|
|
885
|
+
color: var(--amber);
|
|
886
|
+
margin-bottom: 6px;
|
|
749
887
|
}
|
|
750
888
|
h1 {
|
|
751
889
|
margin: 0;
|
|
752
|
-
font-size:
|
|
753
|
-
font-weight:
|
|
890
|
+
font-size: 22px;
|
|
891
|
+
font-weight: 700;
|
|
754
892
|
letter-spacing: -0.02em;
|
|
893
|
+
color: var(--text);
|
|
755
894
|
}
|
|
756
|
-
.meta
|
|
757
|
-
|
|
895
|
+
.masthead-meta {
|
|
896
|
+
margin-top: 10px;
|
|
897
|
+
display: flex;
|
|
898
|
+
flex-wrap: wrap;
|
|
899
|
+
gap: 18px;
|
|
900
|
+
font-size: 11.5px;
|
|
758
901
|
color: var(--muted);
|
|
759
|
-
|
|
902
|
+
}
|
|
903
|
+
.masthead-meta span strong {
|
|
904
|
+
color: var(--text-secondary);
|
|
905
|
+
font-weight: 600;
|
|
760
906
|
}
|
|
761
907
|
.actions-bar {
|
|
762
908
|
display: flex;
|
|
763
|
-
|
|
909
|
+
flex-wrap: wrap;
|
|
910
|
+
gap: 8px;
|
|
764
911
|
align-items: center;
|
|
765
912
|
}
|
|
766
|
-
.btn {
|
|
767
|
-
background: var(--
|
|
768
|
-
color:
|
|
769
|
-
border:
|
|
770
|
-
padding:
|
|
771
|
-
|
|
913
|
+
.btn-primary {
|
|
914
|
+
background: var(--text);
|
|
915
|
+
color: var(--btn-primary-fg);
|
|
916
|
+
border: 1px solid var(--text);
|
|
917
|
+
padding: 9px 14px;
|
|
918
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
919
|
+
font-size: 11px;
|
|
772
920
|
font-weight: 700;
|
|
773
|
-
|
|
921
|
+
letter-spacing: 0.06em;
|
|
922
|
+
text-transform: uppercase;
|
|
774
923
|
cursor: pointer;
|
|
775
924
|
text-decoration: none;
|
|
776
|
-
display: inline-flex;
|
|
777
|
-
align-items: center;
|
|
778
|
-
gap: 6px;
|
|
779
925
|
}
|
|
780
|
-
.btn-
|
|
926
|
+
.btn-primary:hover {
|
|
927
|
+
background: var(--amber);
|
|
928
|
+
border-color: var(--amber);
|
|
929
|
+
color: #050506;
|
|
930
|
+
}
|
|
931
|
+
.btn-secondary {
|
|
781
932
|
background: transparent;
|
|
782
|
-
color: var(--text);
|
|
783
|
-
border: 1px solid var(--
|
|
784
|
-
padding:
|
|
785
|
-
|
|
933
|
+
color: var(--text-secondary);
|
|
934
|
+
border: 1px solid var(--border-strong);
|
|
935
|
+
padding: 9px 14px;
|
|
936
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
937
|
+
font-size: 11px;
|
|
786
938
|
font-weight: 600;
|
|
787
|
-
|
|
939
|
+
letter-spacing: 0.06em;
|
|
940
|
+
text-transform: uppercase;
|
|
788
941
|
cursor: pointer;
|
|
789
942
|
text-decoration: none;
|
|
790
943
|
}
|
|
791
|
-
.
|
|
944
|
+
.btn-secondary:hover {
|
|
945
|
+
border-color: var(--text-secondary);
|
|
946
|
+
color: var(--text);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/* Flush Architectural Telemetry Strip */
|
|
950
|
+
.telemetry-strip {
|
|
792
951
|
display: grid;
|
|
793
|
-
grid-template-columns: repeat(
|
|
794
|
-
|
|
795
|
-
|
|
952
|
+
grid-template-columns: repeat(6, 1fr);
|
|
953
|
+
background: var(--panel);
|
|
954
|
+
border: 1px solid var(--border);
|
|
955
|
+
margin-bottom: 16px;
|
|
796
956
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
border: 1px solid var(--card-border);
|
|
800
|
-
border-radius: 14px;
|
|
801
|
-
padding: 18px 20px;
|
|
957
|
+
@media (max-width: 1100px) {
|
|
958
|
+
.telemetry-strip { grid-template-columns: repeat(3, 1fr); }
|
|
802
959
|
}
|
|
803
|
-
|
|
804
|
-
|
|
960
|
+
@media (max-width: 640px) {
|
|
961
|
+
.telemetry-strip { grid-template-columns: repeat(2, 1fr); }
|
|
962
|
+
}
|
|
963
|
+
.telemetry-cell {
|
|
964
|
+
padding: 16px 20px;
|
|
965
|
+
border-right: 1px solid var(--border);
|
|
966
|
+
border-bottom: 1px solid var(--border);
|
|
967
|
+
}
|
|
968
|
+
.telemetry-cell:last-child { border-right: none; }
|
|
969
|
+
.telemetry-label {
|
|
970
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
971
|
+
font-size: 10px;
|
|
805
972
|
text-transform: uppercase;
|
|
806
|
-
letter-spacing: 0.
|
|
973
|
+
letter-spacing: 0.12em;
|
|
807
974
|
color: var(--muted);
|
|
808
|
-
margin-bottom:
|
|
975
|
+
margin-bottom: 8px;
|
|
809
976
|
}
|
|
810
|
-
.
|
|
811
|
-
font-size: 26px;
|
|
812
|
-
font-weight: 800;
|
|
977
|
+
.telemetry-val {
|
|
813
978
|
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
979
|
+
font-size: 24px;
|
|
980
|
+
font-weight: 700;
|
|
981
|
+
color: var(--text);
|
|
982
|
+
letter-spacing: -0.02em;
|
|
814
983
|
}
|
|
815
|
-
.
|
|
816
|
-
font-size:
|
|
984
|
+
.telemetry-sub {
|
|
985
|
+
font-size: 11px;
|
|
817
986
|
color: var(--muted);
|
|
818
987
|
margin-top: 4px;
|
|
819
988
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
989
|
+
|
|
990
|
+
/* Visualization Grid (2 Columns) */
|
|
991
|
+
.viz-grid {
|
|
992
|
+
display: grid;
|
|
993
|
+
grid-template-columns: 1fr 1.25fr;
|
|
994
|
+
gap: 16px;
|
|
995
|
+
margin-bottom: 16px;
|
|
996
|
+
}
|
|
997
|
+
@media (max-width: 980px) {
|
|
998
|
+
.viz-grid { grid-template-columns: 1fr; }
|
|
826
999
|
}
|
|
827
|
-
.
|
|
1000
|
+
.panel {
|
|
1001
|
+
background: var(--panel);
|
|
1002
|
+
border: 1px solid var(--border);
|
|
1003
|
+
padding: 20px 24px;
|
|
1004
|
+
margin-bottom: 16px;
|
|
1005
|
+
}
|
|
1006
|
+
.viz-grid .panel { margin-bottom: 0; }
|
|
1007
|
+
.panel-header {
|
|
828
1008
|
display: flex;
|
|
829
1009
|
flex-wrap: wrap;
|
|
830
1010
|
justify-content: space-between;
|
|
831
|
-
align-items:
|
|
1011
|
+
align-items: flex-start;
|
|
832
1012
|
gap: 12px;
|
|
1013
|
+
padding-bottom: 14px;
|
|
1014
|
+
border-bottom: 1px solid var(--border);
|
|
833
1015
|
margin-bottom: 16px;
|
|
834
1016
|
}
|
|
1017
|
+
.section-index {
|
|
1018
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
1019
|
+
font-size: 10px;
|
|
1020
|
+
letter-spacing: 0.14em;
|
|
1021
|
+
text-transform: uppercase;
|
|
1022
|
+
color: var(--muted);
|
|
1023
|
+
margin-bottom: 3px;
|
|
1024
|
+
}
|
|
835
1025
|
h2 {
|
|
836
1026
|
margin: 0;
|
|
837
|
-
font-size:
|
|
1027
|
+
font-size: 15px;
|
|
838
1028
|
font-weight: 700;
|
|
1029
|
+
letter-spacing: -0.01em;
|
|
1030
|
+
color: var(--text);
|
|
839
1031
|
}
|
|
840
1032
|
.section-sub {
|
|
841
1033
|
margin: 3px 0 0;
|
|
842
|
-
font-size:
|
|
1034
|
+
font-size: 12px;
|
|
843
1035
|
color: var(--muted);
|
|
844
1036
|
}
|
|
1037
|
+
|
|
1038
|
+
/* Distribution Bars */
|
|
1039
|
+
.dist-row {
|
|
1040
|
+
margin-bottom: 14px;
|
|
1041
|
+
}
|
|
1042
|
+
.dist-row:last-child { margin-bottom: 0; }
|
|
1043
|
+
.dist-header {
|
|
1044
|
+
display: flex;
|
|
1045
|
+
justify-content: space-between;
|
|
1046
|
+
font-size: 11.5px;
|
|
1047
|
+
margin-bottom: 6px;
|
|
1048
|
+
}
|
|
1049
|
+
.dist-track {
|
|
1050
|
+
height: 6px;
|
|
1051
|
+
width: 100%;
|
|
1052
|
+
background: var(--track-bg);
|
|
1053
|
+
display: flex;
|
|
1054
|
+
overflow: hidden;
|
|
1055
|
+
}
|
|
1056
|
+
.dist-seg-primary { background: var(--bar-primary); height: 100%; }
|
|
1057
|
+
.dist-seg-emerald { background: var(--emerald); height: 100%; }
|
|
1058
|
+
.dist-seg-amber { background: var(--amber); height: 100%; }
|
|
1059
|
+
.dist-seg-muted { background: var(--bar-muted); height: 100%; }
|
|
1060
|
+
|
|
1061
|
+
/* Bitcoin Topology Strip */
|
|
1062
|
+
.topo-chain {
|
|
1063
|
+
display: flex;
|
|
1064
|
+
align-items: stretch;
|
|
1065
|
+
gap: 0;
|
|
1066
|
+
overflow-x: auto;
|
|
1067
|
+
padding: 6px 0 4px;
|
|
1068
|
+
}
|
|
1069
|
+
.topo-node {
|
|
1070
|
+
flex: 1;
|
|
1071
|
+
min-width: 135px;
|
|
1072
|
+
background: var(--panel-alt);
|
|
1073
|
+
border: 1px solid var(--border-strong);
|
|
1074
|
+
padding: 12px 14px;
|
|
1075
|
+
}
|
|
1076
|
+
.topo-node-pending {
|
|
1077
|
+
border-style: dashed;
|
|
1078
|
+
border-color: rgba(245, 158, 11, 0.55);
|
|
1079
|
+
}
|
|
1080
|
+
.topo-node-top {
|
|
1081
|
+
display: flex;
|
|
1082
|
+
align-items: center;
|
|
1083
|
+
gap: 6px;
|
|
1084
|
+
margin-bottom: 8px;
|
|
1085
|
+
}
|
|
1086
|
+
.topo-height {
|
|
1087
|
+
font-size: 11px;
|
|
1088
|
+
font-weight: 700;
|
|
1089
|
+
color: var(--text);
|
|
1090
|
+
}
|
|
1091
|
+
.topo-metric {
|
|
1092
|
+
font-size: 14px;
|
|
1093
|
+
font-weight: 700;
|
|
1094
|
+
color: var(--text-secondary);
|
|
1095
|
+
margin-bottom: 4px;
|
|
1096
|
+
}
|
|
1097
|
+
.topo-meta {
|
|
1098
|
+
font-size: 10.5px;
|
|
1099
|
+
color: var(--muted);
|
|
1100
|
+
}
|
|
1101
|
+
.topo-meta a {
|
|
1102
|
+
color: var(--muted);
|
|
1103
|
+
text-decoration: none;
|
|
1104
|
+
border-bottom: 1px dotted var(--muted);
|
|
1105
|
+
}
|
|
1106
|
+
.topo-meta a:hover { color: var(--text); border-bottom-color: var(--text); }
|
|
1107
|
+
.topo-connector {
|
|
1108
|
+
width: 18px;
|
|
1109
|
+
min-width: 18px;
|
|
1110
|
+
display: flex;
|
|
1111
|
+
align-items: center;
|
|
1112
|
+
justify-content: center;
|
|
1113
|
+
position: relative;
|
|
1114
|
+
}
|
|
1115
|
+
.topo-connector::before {
|
|
1116
|
+
content: "";
|
|
1117
|
+
width: 100%;
|
|
1118
|
+
height: 1px;
|
|
1119
|
+
background: var(--border-strong);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/* Status Dots & Tags */
|
|
1123
|
+
.status-cell {
|
|
1124
|
+
display: inline-flex;
|
|
1125
|
+
align-items: center;
|
|
1126
|
+
gap: 7px;
|
|
1127
|
+
white-space: nowrap;
|
|
1128
|
+
}
|
|
1129
|
+
.dot {
|
|
1130
|
+
width: 6px;
|
|
1131
|
+
height: 6px;
|
|
1132
|
+
border-radius: 50%;
|
|
1133
|
+
display: inline-block;
|
|
1134
|
+
flex-shrink: 0;
|
|
1135
|
+
}
|
|
1136
|
+
.dot-settled { background: var(--emerald); }
|
|
1137
|
+
.dot-pending { background: var(--amber); }
|
|
1138
|
+
.dot-alert { background: var(--crimson); }
|
|
1139
|
+
|
|
1140
|
+
.status-pill {
|
|
1141
|
+
display: inline-flex;
|
|
1142
|
+
align-items: center;
|
|
1143
|
+
gap: 8px;
|
|
1144
|
+
padding: 5px 10px;
|
|
1145
|
+
border: 1px solid var(--border-strong);
|
|
1146
|
+
background: var(--panel-alt);
|
|
1147
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
1148
|
+
font-size: 11px;
|
|
1149
|
+
font-weight: 600;
|
|
1150
|
+
}
|
|
1151
|
+
.status-pill-ok { border-color: rgba(16, 185, 129, 0.4); color: var(--text); }
|
|
1152
|
+
.status-pill-alert { border-color: rgba(239, 68, 68, 0.5); color: var(--crimson); }
|
|
1153
|
+
|
|
1154
|
+
.tag {
|
|
1155
|
+
display: inline-block;
|
|
1156
|
+
padding: 2px 6px;
|
|
1157
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
1158
|
+
font-size: 10px;
|
|
1159
|
+
font-weight: 600;
|
|
1160
|
+
letter-spacing: 0.04em;
|
|
1161
|
+
white-space: nowrap;
|
|
1162
|
+
}
|
|
1163
|
+
.tag-neutral {
|
|
1164
|
+
background: var(--tag-bg);
|
|
1165
|
+
border: 1px solid var(--border-strong);
|
|
1166
|
+
color: var(--text-secondary);
|
|
1167
|
+
}
|
|
1168
|
+
.tag-subtle {
|
|
1169
|
+
background: transparent;
|
|
1170
|
+
border: 1px solid var(--border);
|
|
1171
|
+
color: var(--muted);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
.mini-bar-track {
|
|
1175
|
+
width: 72px;
|
|
1176
|
+
height: 4px;
|
|
1177
|
+
background: var(--track-bg);
|
|
1178
|
+
overflow: hidden;
|
|
1179
|
+
}
|
|
1180
|
+
.mini-bar-fill {
|
|
1181
|
+
height: 100%;
|
|
1182
|
+
background: var(--text-secondary);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
/* Filter Bar */
|
|
845
1186
|
.filter-bar {
|
|
846
1187
|
display: flex;
|
|
847
1188
|
flex-wrap: wrap;
|
|
848
|
-
|
|
1189
|
+
justify-content: space-between;
|
|
1190
|
+
gap: 10px;
|
|
849
1191
|
align-items: center;
|
|
850
|
-
margin-bottom:
|
|
1192
|
+
margin-bottom: 14px;
|
|
851
1193
|
}
|
|
852
1194
|
.search-input {
|
|
853
|
-
background:
|
|
854
|
-
border: 1px solid var(--
|
|
1195
|
+
background: var(--bg);
|
|
1196
|
+
border: 1px solid var(--border-strong);
|
|
855
1197
|
color: var(--text);
|
|
856
|
-
padding:
|
|
857
|
-
|
|
858
|
-
font-size:
|
|
859
|
-
min-width:
|
|
1198
|
+
padding: 7px 12px;
|
|
1199
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
1200
|
+
font-size: 11.5px;
|
|
1201
|
+
min-width: 290px;
|
|
1202
|
+
outline: none;
|
|
1203
|
+
}
|
|
1204
|
+
.search-input:focus { border-color: var(--text-secondary); }
|
|
1205
|
+
.tab-group {
|
|
1206
|
+
display: inline-flex;
|
|
1207
|
+
border: 1px solid var(--border-strong);
|
|
1208
|
+
background: var(--bg);
|
|
860
1209
|
}
|
|
861
|
-
.filter-
|
|
862
|
-
background:
|
|
863
|
-
border:
|
|
1210
|
+
.filter-tab {
|
|
1211
|
+
background: transparent;
|
|
1212
|
+
border: none;
|
|
1213
|
+
border-right: 1px solid var(--border);
|
|
864
1214
|
color: var(--muted);
|
|
865
|
-
padding: 6px
|
|
866
|
-
|
|
867
|
-
font-size:
|
|
1215
|
+
padding: 6px 11px;
|
|
1216
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
1217
|
+
font-size: 10.5px;
|
|
868
1218
|
font-weight: 600;
|
|
1219
|
+
text-transform: uppercase;
|
|
1220
|
+
letter-spacing: 0.05em;
|
|
869
1221
|
cursor: pointer;
|
|
870
1222
|
}
|
|
871
|
-
.filter-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
overflow-x: auto;
|
|
1223
|
+
.filter-tab:last-child { border-right: none; }
|
|
1224
|
+
.filter-tab:hover { color: var(--text-secondary); }
|
|
1225
|
+
.filter-tab.active {
|
|
1226
|
+
background: var(--tab-active-bg);
|
|
1227
|
+
color: var(--text);
|
|
1228
|
+
box-shadow: inset 0 -2px 0 var(--amber);
|
|
878
1229
|
}
|
|
1230
|
+
|
|
1231
|
+
/* Tables */
|
|
1232
|
+
.table-wrap { overflow-x: auto; }
|
|
879
1233
|
table {
|
|
880
1234
|
width: 100%;
|
|
881
1235
|
border-collapse: collapse;
|
|
882
|
-
font-size: 13px;
|
|
883
1236
|
}
|
|
884
1237
|
th {
|
|
885
1238
|
text-align: left;
|
|
886
|
-
padding:
|
|
887
|
-
font-
|
|
1239
|
+
padding: 10px 12px;
|
|
1240
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
1241
|
+
font-size: 10px;
|
|
1242
|
+
font-weight: 600;
|
|
888
1243
|
text-transform: uppercase;
|
|
889
|
-
letter-spacing: 0.
|
|
1244
|
+
letter-spacing: 0.1em;
|
|
890
1245
|
color: var(--muted);
|
|
891
|
-
|
|
1246
|
+
background: var(--bg);
|
|
1247
|
+
border-top: 1px solid var(--border);
|
|
1248
|
+
border-bottom: 1px solid var(--border);
|
|
892
1249
|
white-space: nowrap;
|
|
893
1250
|
}
|
|
894
1251
|
td {
|
|
895
|
-
padding: 12px;
|
|
896
|
-
border-bottom: 1px solid
|
|
1252
|
+
padding: 11px 12px;
|
|
1253
|
+
border-bottom: 1px solid var(--border);
|
|
897
1254
|
vertical-align: middle;
|
|
898
1255
|
}
|
|
899
|
-
tr:hover td {
|
|
900
|
-
background: rgba(255, 255, 255, 0.02);
|
|
901
|
-
}
|
|
902
|
-
.mono {
|
|
903
|
-
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
904
|
-
font-size: 12px;
|
|
905
|
-
}
|
|
1256
|
+
tr:hover td { background: var(--row-hover); }
|
|
906
1257
|
.hash-full {
|
|
907
1258
|
word-break: break-all;
|
|
908
|
-
color:
|
|
909
|
-
font-size: 11.5px;
|
|
910
|
-
}
|
|
911
|
-
.badge {
|
|
912
|
-
display: inline-block;
|
|
913
|
-
padding: 3px 9px;
|
|
914
|
-
border-radius: 999px;
|
|
1259
|
+
color: var(--text-secondary);
|
|
915
1260
|
font-size: 11px;
|
|
916
|
-
font-weight: 700;
|
|
917
|
-
letter-spacing: 0.03em;
|
|
918
|
-
white-space: nowrap;
|
|
919
1261
|
}
|
|
920
|
-
.
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
.
|
|
926
|
-
|
|
1262
|
+
.signer-key-header {
|
|
1263
|
+
display: flex;
|
|
1264
|
+
align-items: center;
|
|
1265
|
+
gap: 8px;
|
|
1266
|
+
}
|
|
1267
|
+
.action-link {
|
|
1268
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
1269
|
+
font-size: 10.5px;
|
|
1270
|
+
font-weight: 600;
|
|
1271
|
+
color: var(--text-secondary);
|
|
927
1272
|
text-decoration: none;
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
border: 1px solid rgba(245,176,65,0.35);
|
|
932
|
-
border-radius: 6px;
|
|
933
|
-
background: var(--gold-dim);
|
|
1273
|
+
padding: 4px 8px;
|
|
1274
|
+
border: 1px solid var(--border-strong);
|
|
1275
|
+
background: var(--bg);
|
|
934
1276
|
white-space: nowrap;
|
|
935
1277
|
}
|
|
1278
|
+
.action-link:hover {
|
|
1279
|
+
border-color: var(--amber);
|
|
1280
|
+
color: var(--amber);
|
|
1281
|
+
}
|
|
936
1282
|
.tx-link {
|
|
937
|
-
color:
|
|
1283
|
+
color: var(--muted);
|
|
938
1284
|
text-decoration: none;
|
|
939
|
-
font-size:
|
|
1285
|
+
font-size: 10.5px;
|
|
940
1286
|
display: inline-block;
|
|
941
1287
|
margin-top: 2px;
|
|
942
1288
|
}
|
|
943
|
-
.tx-link:hover { color: var(--
|
|
1289
|
+
.tx-link:hover { color: var(--text); }
|
|
1290
|
+
.empty-cell {
|
|
1291
|
+
text-align: center;
|
|
1292
|
+
color: var(--muted);
|
|
1293
|
+
padding: 24px;
|
|
1294
|
+
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
|
|
1295
|
+
font-size: 11.5px;
|
|
1296
|
+
}
|
|
944
1297
|
@media print {
|
|
945
|
-
body { background:
|
|
1298
|
+
body { background: var(--bg) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; padding: 10px; }
|
|
946
1299
|
.no-print { display: none !important; }
|
|
947
1300
|
}
|
|
948
1301
|
</style>
|
|
949
1302
|
</head>
|
|
950
1303
|
<body>
|
|
951
1304
|
<div class="container">
|
|
952
|
-
<
|
|
1305
|
+
<header class="masthead">
|
|
953
1306
|
<div>
|
|
954
|
-
<div class="
|
|
955
|
-
<h1>Cryptographic Data Integrity & Timechain
|
|
956
|
-
<div class="meta
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1307
|
+
<div class="eyebrow">REL-TIME SOVEREIGN PROVENANCE PROTOCOL // FORENSIC AUDIT LEDGER</div>
|
|
1308
|
+
<h1>Cryptographic Data Integrity & Timechain Settlement Report</h1>
|
|
1309
|
+
<div class="masthead-meta mono">
|
|
1310
|
+
<span>GENERATED UTC: <strong>${escapeHtml(generatedUtc)}</strong></span>
|
|
1311
|
+
<span>SLA TIER: <strong>${escapeHtml(acct.tier || "Active")}</strong></span>
|
|
1312
|
+
<span>CREDENTIAL: <strong>${escapeHtml(acct.masked_api_key || "rel_key_***")}</strong></span>
|
|
960
1313
|
</div>
|
|
961
1314
|
</div>
|
|
962
1315
|
<div class="actions-bar no-print">
|
|
963
|
-
<
|
|
964
|
-
<
|
|
1316
|
+
<button id="themeToggleBtn" class="btn-secondary" onclick="toggleTheme()">[ ☀ LIGHT MODE ]</button>
|
|
1317
|
+
<a class="btn-secondary" href="https://rel-time.com/verify" target="_blank" rel="noopener">[ OPEN VERIFIER ↗ ]</a>
|
|
1318
|
+
<button class="btn-primary" onclick="window.print()">[ EXPORT PDF CERTIFICATE ]</button>
|
|
965
1319
|
</div>
|
|
966
|
-
</
|
|
1320
|
+
</header>
|
|
967
1321
|
|
|
968
|
-
<div class="
|
|
969
|
-
<div class="
|
|
970
|
-
<div class="
|
|
971
|
-
<div class="
|
|
972
|
-
<div class="
|
|
1322
|
+
<div class="telemetry-strip">
|
|
1323
|
+
<div class="telemetry-cell">
|
|
1324
|
+
<div class="telemetry-label">Audited Scope</div>
|
|
1325
|
+
<div class="telemetry-val">${Number(sum.matched_entries_count || 0).toLocaleString()}</div>
|
|
1326
|
+
<div class="telemetry-sub mono">LIFETIME: ${Number(acct.lifetime_anchors || 0).toLocaleString()}</div>
|
|
973
1327
|
</div>
|
|
974
|
-
<div class="
|
|
975
|
-
<div class="
|
|
976
|
-
<div class="
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1328
|
+
<div class="telemetry-cell">
|
|
1329
|
+
<div class="telemetry-label">Timechain Finality</div>
|
|
1330
|
+
<div class="telemetry-val">${settledCount.toLocaleString()} <span style="font-size:14px;color:var(--muted)">/ ${Number(
|
|
1331
|
+
sum.matched_entries_count || 0
|
|
1332
|
+
)}</span></div>
|
|
1333
|
+
<div class="telemetry-sub mono">${escapeHtml(sum.settlement_rate_pct || "0%")} SETTLED · ${pendingCount} QUEUED</div>
|
|
980
1334
|
</div>
|
|
981
|
-
<div class="
|
|
982
|
-
<div class="
|
|
983
|
-
<div class="
|
|
984
|
-
<div class="
|
|
985
|
-
btc.latest_settled_block ? ` ·
|
|
1335
|
+
<div class="telemetry-cell">
|
|
1336
|
+
<div class="telemetry-label">Bitcoin Blocks</div>
|
|
1337
|
+
<div class="telemetry-val">${Number(btc.unique_blocks_count || 0)}</div>
|
|
1338
|
+
<div class="telemetry-sub mono">${Number(btc.unique_txids_count || 0)} OP_RETURN TXS${
|
|
1339
|
+
btc.latest_settled_block ? ` · #${escapeHtml(btc.latest_settled_block)}` : ""
|
|
986
1340
|
}</div>
|
|
987
1341
|
</div>
|
|
988
|
-
<div class="
|
|
989
|
-
<div class="
|
|
990
|
-
<div class="
|
|
991
|
-
|
|
992
|
-
)}</div>
|
|
993
|
-
<div class="kpi-sub">Standard Integrity / Temporal VDF</div>
|
|
1342
|
+
<div class="telemetry-cell">
|
|
1343
|
+
<div class="telemetry-label">Mode Split</div>
|
|
1344
|
+
<div class="telemetry-val">${standardCount} <span style="font-size:14px;color:var(--muted)">:</span> ${temporalCount}</div>
|
|
1345
|
+
<div class="telemetry-sub mono">STANDARD : TEMPORAL VDF</div>
|
|
994
1346
|
</div>
|
|
995
|
-
<div class="
|
|
996
|
-
<div class="
|
|
997
|
-
<div class="
|
|
998
|
-
<div class="
|
|
1347
|
+
<div class="telemetry-cell">
|
|
1348
|
+
<div class="telemetry-label">Passkey Authorities</div>
|
|
1349
|
+
<div class="telemetry-val">${signers.length}</div>
|
|
1350
|
+
<div class="telemetry-sub mono">${biometricCount} BIOMETRIC SIGNATURES</div>
|
|
999
1351
|
</div>
|
|
1000
|
-
<div class="
|
|
1001
|
-
<div class="
|
|
1002
|
-
<div class="
|
|
1003
|
-
|
|
1352
|
+
<div class="telemetry-cell">
|
|
1353
|
+
<div class="telemetry-label">Available Fuel</div>
|
|
1354
|
+
<div class="telemetry-val" style="color:var(--amber)">${Number(
|
|
1355
|
+
acct.hash_credits_remaining || 0
|
|
1356
|
+
).toLocaleString()}</div>
|
|
1357
|
+
<div class="telemetry-sub mono">HASH CREDITS REMAINING</div>
|
|
1358
|
+
</div>
|
|
1359
|
+
</div>
|
|
1360
|
+
|
|
1361
|
+
<div class="viz-grid">
|
|
1362
|
+
<div class="panel">
|
|
1363
|
+
<div class="panel-header" style="margin-bottom:14px;padding-bottom:10px">
|
|
1364
|
+
<div>
|
|
1365
|
+
<div class="section-index">TELEMETRY // DISTRIBUTION METRICS</div>
|
|
1366
|
+
<h2>Pipeline & Attestation Ratios</h2>
|
|
1367
|
+
</div>
|
|
1368
|
+
</div>
|
|
1369
|
+
<div class="dist-row">
|
|
1370
|
+
<div class="dist-header mono">
|
|
1371
|
+
<span>BITCOIN FINALITY (${settledPct}% SETTLED)</span>
|
|
1372
|
+
<span class="muted">${settledCount} SETTLED / ${pendingCount} PENDING</span>
|
|
1373
|
+
</div>
|
|
1374
|
+
<div class="dist-track">
|
|
1375
|
+
<div class="dist-seg-emerald" style="width:${settledPct}%"></div>
|
|
1376
|
+
<div class="dist-seg-amber" style="width:${100 - settledPct}%"></div>
|
|
1377
|
+
</div>
|
|
1378
|
+
</div>
|
|
1379
|
+
<div class="dist-row">
|
|
1380
|
+
<div class="dist-header mono">
|
|
1381
|
+
<span>HASHING ARCHITECTURE (${standardPct}% STANDARD)</span>
|
|
1382
|
+
<span class="muted">${standardCount} INTEGRITY / ${temporalCount} TEMPORAL VDF</span>
|
|
1383
|
+
</div>
|
|
1384
|
+
<div class="dist-track">
|
|
1385
|
+
<div class="dist-seg-primary" style="width:${standardPct}%"></div>
|
|
1386
|
+
<div class="dist-seg-muted" style="width:${100 - standardPct}%"></div>
|
|
1387
|
+
</div>
|
|
1388
|
+
</div>
|
|
1389
|
+
<div class="dist-row">
|
|
1390
|
+
<div class="dist-header mono">
|
|
1391
|
+
<span>HARDWARE PASSKEY ATTESTATION (${biometricPct}%)</span>
|
|
1392
|
+
<span class="muted">${biometricCount} FIDO2 SIGNED / ${machineCount} MACHINE</span>
|
|
1393
|
+
</div>
|
|
1394
|
+
<div class="dist-track">
|
|
1395
|
+
<div class="dist-seg-primary" style="width:${biometricPct}%"></div>
|
|
1396
|
+
<div class="dist-seg-muted" style="width:${100 - biometricPct}%"></div>
|
|
1397
|
+
</div>
|
|
1398
|
+
</div>
|
|
1399
|
+
</div>
|
|
1400
|
+
|
|
1401
|
+
<div class="panel">
|
|
1402
|
+
<div class="panel-header" style="margin-bottom:14px;padding-bottom:10px">
|
|
1403
|
+
<div>
|
|
1404
|
+
<div class="section-index">TIMECHAIN TOPOLOGY // MERKLE BATCH ANCHORS</div>
|
|
1405
|
+
<h2>Bitcoin Block Settlement Sequence</h2>
|
|
1406
|
+
</div>
|
|
1407
|
+
<span class="mono muted" style="font-size:11px">SHA-256 MERKLE ROOT ➔ OP_RETURN</span>
|
|
1408
|
+
</div>
|
|
1409
|
+
<div class="topo-chain">
|
|
1410
|
+
${
|
|
1411
|
+
topologyNodesHtml ||
|
|
1412
|
+
`<div class="empty-cell" style="width:100%;padding:16px">No settled Bitcoin blocks in current filter window.</div>`
|
|
1413
|
+
}
|
|
1414
|
+
</div>
|
|
1004
1415
|
</div>
|
|
1005
1416
|
</div>
|
|
1006
1417
|
|
|
1007
1418
|
${localVerificationHtml}
|
|
1008
1419
|
|
|
1009
|
-
<
|
|
1010
|
-
<div class="
|
|
1420
|
+
<section class="panel">
|
|
1421
|
+
<div class="panel-header">
|
|
1011
1422
|
<div>
|
|
1012
|
-
<
|
|
1013
|
-
<
|
|
1423
|
+
<div class="section-index">SECTION ${signerSectionNum} // CORPORATE HARDWARE PASSKEY REGISTRY</div>
|
|
1424
|
+
<h2>Authorized WebAuthn / FIDO2 Signer Authorities</h2>
|
|
1425
|
+
<p class="section-sub">Non-exportable Secure Enclave & YubiKey COSE public keys recorded across this corporate credential</p>
|
|
1014
1426
|
</div>
|
|
1015
1427
|
</div>
|
|
1016
1428
|
<div class="table-wrap">
|
|
1017
1429
|
<table>
|
|
1018
1430
|
<thead>
|
|
1019
1431
|
<tr>
|
|
1020
|
-
<th>#</th>
|
|
1432
|
+
<th style="width:42px">#</th>
|
|
1021
1433
|
<th>Hardware Signer Public Key (COSE Hex)</th>
|
|
1022
|
-
<th>
|
|
1023
|
-
<th>
|
|
1024
|
-
<th>Latest
|
|
1434
|
+
<th style="width:200px">Attested Volume</th>
|
|
1435
|
+
<th style="width:130px">Finality Ratio</th>
|
|
1436
|
+
<th style="width:190px">Latest Attestation (UTC)</th>
|
|
1025
1437
|
</tr>
|
|
1026
1438
|
</thead>
|
|
1027
1439
|
<tbody>
|
|
@@ -1029,46 +1441,39 @@ function generateAuditReportHtml(audit) {
|
|
|
1029
1441
|
</tbody>
|
|
1030
1442
|
</table>
|
|
1031
1443
|
</div>
|
|
1032
|
-
</
|
|
1444
|
+
</section>
|
|
1033
1445
|
|
|
1034
|
-
<
|
|
1035
|
-
<div class="
|
|
1446
|
+
<section class="panel">
|
|
1447
|
+
<div class="panel-header">
|
|
1036
1448
|
<div>
|
|
1037
|
-
<
|
|
1038
|
-
<
|
|
1449
|
+
<div class="section-index">SECTION ${ledgerSectionNum} // CHRONOLOGICAL TIMECHAIN LEDGER</div>
|
|
1450
|
+
<h2>Cryptographic Anchor & VDF Sequence Log</h2>
|
|
1451
|
+
<p class="section-sub">Immutable record of SHA-256 payload digests, VDF thermodynamic proofs (T_index), and Bitcoin Merkle settlements</p>
|
|
1039
1452
|
</div>
|
|
1040
1453
|
</div>
|
|
1041
1454
|
<div class="filter-bar no-print">
|
|
1042
|
-
<
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
<
|
|
1051
|
-
modes.biometric_passkey_count || 0
|
|
1052
|
-
)})</button>
|
|
1053
|
-
<button class="filter-pill" onclick="setPill(this, 'mode-temporal')">Temporal VDF (${Number(
|
|
1054
|
-
modes.temporal_vdf_count || 0
|
|
1055
|
-
)})</button>
|
|
1056
|
-
<button class="filter-pill" onclick="setPill(this, 'mode-standard')">Standard (${Number(
|
|
1057
|
-
modes.standard_count || 0
|
|
1058
|
-
)})</button>
|
|
1455
|
+
<div class="tab-group">
|
|
1456
|
+
<button class="filter-tab active" onclick="setPill(this, 'all')">All (${entries.length})</button>
|
|
1457
|
+
<button class="filter-tab" onclick="setPill(this, 'status-settled')">Settled (${settledCount})</button>
|
|
1458
|
+
<button class="filter-tab" onclick="setPill(this, 'status-pending')">Pending (${pendingCount})</button>
|
|
1459
|
+
<button class="filter-tab" onclick="setPill(this, 'mode-biometric')">Passkey (${biometricCount})</button>
|
|
1460
|
+
<button class="filter-tab" onclick="setPill(this, 'mode-temporal')">Temporal VDF (${temporalCount})</button>
|
|
1461
|
+
<button class="filter-tab" onclick="setPill(this, 'mode-standard')">Standard (${standardCount})</button>
|
|
1462
|
+
</div>
|
|
1463
|
+
<input id="searchInput" class="search-input" type="text" placeholder="QUERY SHA-256 / T_INDEX / PASSKEY / TXID / BLOCK..." oninput="filterLedger()" />
|
|
1059
1464
|
</div>
|
|
1060
1465
|
<div class="table-wrap">
|
|
1061
1466
|
<table>
|
|
1062
1467
|
<thead>
|
|
1063
1468
|
<tr>
|
|
1064
|
-
<th>#</th>
|
|
1469
|
+
<th style="width:42px">#</th>
|
|
1065
1470
|
<th>Timestamp (UTC)</th>
|
|
1066
|
-
<th>
|
|
1067
|
-
<th>Mode
|
|
1068
|
-
<th>Client / File SHA-256</th>
|
|
1069
|
-
<th>Thermodynamic Proof (
|
|
1471
|
+
<th>Finality</th>
|
|
1472
|
+
<th>Mode / Authority</th>
|
|
1473
|
+
<th>Client / File SHA-256 Digest</th>
|
|
1474
|
+
<th>Thermodynamic Proof (T_Index)</th>
|
|
1070
1475
|
<th>Bitcoin Settlement</th>
|
|
1071
|
-
<th>
|
|
1476
|
+
<th style="text-align:right">Proof</th>
|
|
1072
1477
|
</tr>
|
|
1073
1478
|
</thead>
|
|
1074
1479
|
<tbody id="ledgerBody">
|
|
@@ -1076,14 +1481,36 @@ function generateAuditReportHtml(audit) {
|
|
|
1076
1481
|
</tbody>
|
|
1077
1482
|
</table>
|
|
1078
1483
|
</div>
|
|
1079
|
-
</
|
|
1484
|
+
</section>
|
|
1080
1485
|
</div>
|
|
1081
1486
|
|
|
1082
1487
|
<script>
|
|
1083
1488
|
let currentFilter = 'all';
|
|
1489
|
+
function applyTheme(theme) {
|
|
1490
|
+
const root = document.documentElement;
|
|
1491
|
+
const btn = document.getElementById('themeToggleBtn');
|
|
1492
|
+
if (theme === 'light') {
|
|
1493
|
+
root.setAttribute('data-theme', 'light');
|
|
1494
|
+
if (btn) btn.textContent = '[ ☾ DARK MODE ]';
|
|
1495
|
+
} else {
|
|
1496
|
+
root.setAttribute('data-theme', 'dark');
|
|
1497
|
+
if (btn) btn.textContent = '[ ☀ LIGHT MODE ]';
|
|
1498
|
+
}
|
|
1499
|
+
try { localStorage.setItem('reltime_audit_theme', theme); } catch (e) {}
|
|
1500
|
+
}
|
|
1501
|
+
function toggleTheme() {
|
|
1502
|
+
const current = document.documentElement.getAttribute('data-theme') || 'dark';
|
|
1503
|
+
applyTheme(current === 'dark' ? 'light' : 'dark');
|
|
1504
|
+
}
|
|
1505
|
+
(function initTheme() {
|
|
1506
|
+
try {
|
|
1507
|
+
const saved = localStorage.getItem('reltime_audit_theme');
|
|
1508
|
+
if (saved === 'light' || saved === 'dark') applyTheme(saved);
|
|
1509
|
+
} catch (e) {}
|
|
1510
|
+
})();
|
|
1084
1511
|
function setPill(btn, filter) {
|
|
1085
1512
|
currentFilter = filter;
|
|
1086
|
-
document.querySelectorAll('.filter-
|
|
1513
|
+
document.querySelectorAll('.filter-tab').forEach(el => el.classList.remove('active'));
|
|
1087
1514
|
btn.classList.add('active');
|
|
1088
1515
|
filterLedger();
|
|
1089
1516
|
}
|
|
@@ -1100,11 +1527,545 @@ function generateAuditReportHtml(audit) {
|
|
|
1100
1527
|
</html>`;
|
|
1101
1528
|
}
|
|
1102
1529
|
|
|
1530
|
+
function inferMimeTypeFromFileName(fileName) {
|
|
1531
|
+
if (!fileName || fileName === "Zero-Knowledge Hash Payload") {
|
|
1532
|
+
return "application/octet-stream (Zero-Knowledge)";
|
|
1533
|
+
}
|
|
1534
|
+
const lower = String(fileName).toLowerCase();
|
|
1535
|
+
if (lower.endsWith(".pdf")) return "application/pdf";
|
|
1536
|
+
if (lower.endsWith(".png")) return "image/png";
|
|
1537
|
+
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
1538
|
+
if (lower.endsWith(".webp")) return "image/webp";
|
|
1539
|
+
if (lower.endsWith(".svg")) return "image/svg+xml";
|
|
1540
|
+
if (lower.endsWith(".json")) return "application/json";
|
|
1541
|
+
if (lower.endsWith(".csv")) return "text/csv";
|
|
1542
|
+
if (lower.endsWith(".txt") || lower.endsWith(".md")) return "text/plain";
|
|
1543
|
+
if (lower.endsWith(".mp4")) return "video/mp4";
|
|
1544
|
+
if (lower.endsWith(".mp3")) return "audio/mpeg";
|
|
1545
|
+
if (lower.endsWith(".wav")) return "audio/wav";
|
|
1546
|
+
if (lower.endsWith(".zip")) return "application/zip";
|
|
1547
|
+
return "application/octet-stream";
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
function generateProvenanceCertificateHtml(input = {}) {
|
|
1551
|
+
const fileName = escapeHtml(input.fileName || "Zero-Knowledge Hash Payload");
|
|
1552
|
+
const fileSize = escapeHtml(input.fileSize || "Zero-Knowledge (32-Byte Digest)");
|
|
1553
|
+
const mimeType = escapeHtml(input.mimeType || inferMimeTypeFromFileName(input.fileName));
|
|
1554
|
+
|
|
1555
|
+
const originalHash = escapeHtml(String(input.originalFileHash || input.attestedHash || "").toLowerCase());
|
|
1556
|
+
const attestedHash = escapeHtml(String(input.attestedHash || input.originalFileHash || "").toLowerCase());
|
|
1557
|
+
const signerPubKey = input.signerPublicKey ? escapeHtml(String(input.signerPublicKey)) : null;
|
|
1558
|
+
|
|
1559
|
+
const isStandard = String(input.anchorMode || "standard").toLowerCase() === "standard";
|
|
1560
|
+
const modeLabel = isStandard
|
|
1561
|
+
? "MODE: STANDARD (DATA INTEGRITY)"
|
|
1562
|
+
: "MODE: TEMPORAL (VDF SEQUENCE)";
|
|
1563
|
+
|
|
1564
|
+
const tIndex = escapeHtml(String(input.tIndex || "PENDING VDF SNAPSHOT"));
|
|
1565
|
+
const formattedTicks =
|
|
1566
|
+
typeof input.vdfTicks === "number"
|
|
1567
|
+
? `${input.vdfTicks.toLocaleString()} Ticks`
|
|
1568
|
+
: escapeHtml(String(input.vdfTicks || "0 Ticks"));
|
|
1569
|
+
|
|
1570
|
+
const isSettled = input.status === "settled" || Boolean(input.blockHeight);
|
|
1571
|
+
const blockHeightDisplay = input.blockHeight
|
|
1572
|
+
? `#${Number(input.blockHeight).toLocaleString()}`
|
|
1573
|
+
: "PENDING NEXT BITCOIN BLOCK";
|
|
1574
|
+
const blockTimeDisplay = escapeHtml(
|
|
1575
|
+
input.blockTimeUtc || (isSettled ? "CONFIRMED ON BITCOIN MAINNET" : "PENDING BLOCK HEADER UTC")
|
|
1576
|
+
);
|
|
1577
|
+
const merkleProofDisplay =
|
|
1578
|
+
typeof input.merkleProofCount === "number" && input.merkleProofCount > 0
|
|
1579
|
+
? `[ ${input.merkleProofCount} SIBLING HASHES ]`
|
|
1580
|
+
: isSettled
|
|
1581
|
+
? "[ INCLUDED IN MERKLE ROOT ]"
|
|
1582
|
+
: "PENDING";
|
|
1583
|
+
const txidDisplay = escapeHtml(String(input.txid || "PENDING NEXT BITCOIN BLOCK"));
|
|
1584
|
+
const finalityValueClass = isSettled ? "data-value green" : "data-value orange";
|
|
1585
|
+
|
|
1586
|
+
return `<!DOCTYPE html>
|
|
1587
|
+
<html lang="en">
|
|
1588
|
+
<head>
|
|
1589
|
+
<meta charset="UTF-8">
|
|
1590
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
|
|
1591
|
+
<title>Rel-Time Sovereign Provenance Certificate - ${fileName}</title>
|
|
1592
|
+
<style>
|
|
1593
|
+
* { box-sizing: border-box; }
|
|
1594
|
+
:root {
|
|
1595
|
+
--bg-body: #050507;
|
|
1596
|
+
--bg-monument: linear-gradient(180deg, #181a20 0%, #0d0e12 50%, #08090c 100%);
|
|
1597
|
+
--border-monument: #3f3f46;
|
|
1598
|
+
--monument-shadow: inset 0 1px 0 rgba(255,255,255,0.15), 0 30px 70px rgba(0,0,0,0.95);
|
|
1599
|
+
--bg-plate: rgba(0, 0, 0, 0.45);
|
|
1600
|
+
--border-plate: #27272a;
|
|
1601
|
+
--plate-title: #d4d4d8;
|
|
1602
|
+
--text-label: #71717a;
|
|
1603
|
+
--text-value: #f4f4f5;
|
|
1604
|
+
--bg-value: #050507;
|
|
1605
|
+
--border-value: #27272a;
|
|
1606
|
+
--accent-orange: #fb923c;
|
|
1607
|
+
--accent-green: #4ade80;
|
|
1608
|
+
--accent-cyan: #22d3ee;
|
|
1609
|
+
--anchored-box-bg: rgba(251, 146, 60, 0.06);
|
|
1610
|
+
--anchored-box-border: rgba(251, 146, 60, 0.65);
|
|
1611
|
+
--anchored-badge-bg: rgba(251, 146, 60, 0.15);
|
|
1612
|
+
--mode-pill-bg: #18181b;
|
|
1613
|
+
--mode-pill-border: #52525b;
|
|
1614
|
+
--mode-pill-color: #f4f4f5;
|
|
1615
|
+
--theme-btn-bg: #27272a;
|
|
1616
|
+
--theme-btn-border: #52525b;
|
|
1617
|
+
--theme-btn-color: #f4f4f5;
|
|
1618
|
+
--solar-shadow: drop-shadow(0 0 25px rgba(255, 95, 31, 0.45));
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
body.light-mode {
|
|
1622
|
+
--bg-body: #e4e4e7;
|
|
1623
|
+
--bg-monument: #ffffff;
|
|
1624
|
+
--border-monument: #cbd5e1;
|
|
1625
|
+
--monument-shadow: 0 20px 45px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.05);
|
|
1626
|
+
--bg-plate: #f8fafc;
|
|
1627
|
+
--border-plate: #e2e8f0;
|
|
1628
|
+
--plate-title: #0f172a;
|
|
1629
|
+
--text-label: #64748b;
|
|
1630
|
+
--text-value: #09090b;
|
|
1631
|
+
--bg-value: #ffffff;
|
|
1632
|
+
--border-value: #cbd5e1;
|
|
1633
|
+
--accent-orange: #ea580c;
|
|
1634
|
+
--accent-green: #15803d;
|
|
1635
|
+
--accent-cyan: #0891b2;
|
|
1636
|
+
--anchored-box-bg: rgba(234, 88, 12, 0.08);
|
|
1637
|
+
--anchored-box-border: rgba(234, 88, 12, 0.75);
|
|
1638
|
+
--anchored-badge-bg: rgba(234, 88, 12, 0.12);
|
|
1639
|
+
--mode-pill-bg: #f1f5f9;
|
|
1640
|
+
--mode-pill-border: #cbd5e1;
|
|
1641
|
+
--mode-pill-color: #0f172a;
|
|
1642
|
+
--theme-btn-bg: #f1f5f9;
|
|
1643
|
+
--theme-btn-border: #cbd5e1;
|
|
1644
|
+
--theme-btn-color: #0f172a;
|
|
1645
|
+
--solar-shadow: drop-shadow(0 0 20px rgba(234, 88, 12, 0.3));
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
body {
|
|
1649
|
+
background-color: var(--bg-body);
|
|
1650
|
+
color: var(--text-value);
|
|
1651
|
+
font-family: 'Fira Code', 'SFMono-Regular', Consolas, Menlo, monospace;
|
|
1652
|
+
margin: 0;
|
|
1653
|
+
padding: 2rem 1rem;
|
|
1654
|
+
display: flex;
|
|
1655
|
+
flex-direction: column;
|
|
1656
|
+
align-items: center;
|
|
1657
|
+
transition: background-color 0.3s ease, color 0.3s ease;
|
|
1658
|
+
min-height: 100vh;
|
|
1659
|
+
overflow-x: hidden;
|
|
1660
|
+
width: 100%;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
.monument {
|
|
1664
|
+
max-width: 960px;
|
|
1665
|
+
width: 100%;
|
|
1666
|
+
background: var(--bg-monument);
|
|
1667
|
+
border: 2px solid var(--border-monument);
|
|
1668
|
+
box-shadow: var(--monument-shadow);
|
|
1669
|
+
border-radius: 6px;
|
|
1670
|
+
padding: 2.5rem;
|
|
1671
|
+
position: relative;
|
|
1672
|
+
transition: all 0.3s ease;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
.header-bar {
|
|
1676
|
+
display: flex;
|
|
1677
|
+
justify-content: space-between;
|
|
1678
|
+
align-items: flex-start;
|
|
1679
|
+
border-bottom: 2px solid var(--border-plate);
|
|
1680
|
+
padding-bottom: 1.5rem;
|
|
1681
|
+
margin-bottom: 2rem;
|
|
1682
|
+
gap: 1.5rem;
|
|
1683
|
+
flex-wrap: wrap;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
.brand-group { display: flex; flex-direction: column; }
|
|
1687
|
+
.brand-logo {
|
|
1688
|
+
display: inline-flex;
|
|
1689
|
+
align-items: center;
|
|
1690
|
+
gap: 0.3rem;
|
|
1691
|
+
font-size: 1.5rem;
|
|
1692
|
+
font-weight: 800;
|
|
1693
|
+
letter-spacing: 0.2em;
|
|
1694
|
+
line-height: 1.2;
|
|
1695
|
+
}
|
|
1696
|
+
.logo-bracket { color: var(--accent-orange); font-weight: 900; }
|
|
1697
|
+
.logo-text { color: var(--text-value); letter-spacing: 0.22em; }
|
|
1698
|
+
.brand-sub {
|
|
1699
|
+
margin: 0.45rem 0 0 0;
|
|
1700
|
+
color: var(--text-label);
|
|
1701
|
+
font-size: 0.68rem;
|
|
1702
|
+
letter-spacing: 0.16em;
|
|
1703
|
+
text-transform: uppercase;
|
|
1704
|
+
}
|
|
1705
|
+
.header-actions {
|
|
1706
|
+
display: flex;
|
|
1707
|
+
align-items: center;
|
|
1708
|
+
gap: 0.75rem;
|
|
1709
|
+
flex-wrap: wrap;
|
|
1710
|
+
}
|
|
1711
|
+
.mode-pill {
|
|
1712
|
+
background: var(--mode-pill-bg);
|
|
1713
|
+
border: 1px solid var(--mode-pill-border);
|
|
1714
|
+
color: var(--mode-pill-color);
|
|
1715
|
+
padding: 0.4rem 0.8rem;
|
|
1716
|
+
font-size: 0.7rem;
|
|
1717
|
+
letter-spacing: 0.15em;
|
|
1718
|
+
text-transform: uppercase;
|
|
1719
|
+
font-weight: 600;
|
|
1720
|
+
border-radius: 4px;
|
|
1721
|
+
white-space: nowrap;
|
|
1722
|
+
}
|
|
1723
|
+
.theme-btn {
|
|
1724
|
+
background: var(--theme-btn-bg);
|
|
1725
|
+
border: 1px solid var(--theme-btn-border);
|
|
1726
|
+
color: var(--theme-btn-color);
|
|
1727
|
+
padding: 0.4rem 0.8rem;
|
|
1728
|
+
font-size: 0.7rem;
|
|
1729
|
+
letter-spacing: 0.15em;
|
|
1730
|
+
text-transform: uppercase;
|
|
1731
|
+
font-weight: 700;
|
|
1732
|
+
cursor: pointer;
|
|
1733
|
+
border-radius: 4px;
|
|
1734
|
+
transition: all 0.2s ease;
|
|
1735
|
+
font-family: inherit;
|
|
1736
|
+
display: inline-flex;
|
|
1737
|
+
align-items: center;
|
|
1738
|
+
gap: 0.35rem;
|
|
1739
|
+
}
|
|
1740
|
+
.theme-btn:hover {
|
|
1741
|
+
border-color: var(--accent-orange);
|
|
1742
|
+
color: var(--accent-orange);
|
|
1743
|
+
}
|
|
1744
|
+
.plate {
|
|
1745
|
+
background: var(--bg-plate);
|
|
1746
|
+
border: 1px solid var(--border-plate);
|
|
1747
|
+
box-shadow: inset 0 2px 6px rgba(0,0,0,0.4);
|
|
1748
|
+
padding: 1.5rem;
|
|
1749
|
+
margin-bottom: 1.75rem;
|
|
1750
|
+
border-radius: 4px;
|
|
1751
|
+
transition: all 0.3s ease;
|
|
1752
|
+
}
|
|
1753
|
+
.plate-title {
|
|
1754
|
+
color: var(--plate-title);
|
|
1755
|
+
font-size: 0.75rem;
|
|
1756
|
+
font-weight: bold;
|
|
1757
|
+
letter-spacing: 0.2em;
|
|
1758
|
+
border-bottom: 1px solid var(--border-plate);
|
|
1759
|
+
padding-bottom: 0.6rem;
|
|
1760
|
+
margin-bottom: 1.25rem;
|
|
1761
|
+
text-transform: uppercase;
|
|
1762
|
+
display: flex;
|
|
1763
|
+
justify-content: space-between;
|
|
1764
|
+
align-items: center;
|
|
1765
|
+
flex-wrap: wrap;
|
|
1766
|
+
gap: 0.5rem;
|
|
1767
|
+
}
|
|
1768
|
+
.plate-subtitle {
|
|
1769
|
+
font-size: 0.65rem;
|
|
1770
|
+
color: var(--text-label);
|
|
1771
|
+
letter-spacing: 0.12em;
|
|
1772
|
+
}
|
|
1773
|
+
.data-row { margin-bottom: 1rem; }
|
|
1774
|
+
.data-row:last-child { margin-bottom: 0; }
|
|
1775
|
+
.data-label {
|
|
1776
|
+
color: var(--text-label);
|
|
1777
|
+
font-size: 0.68rem;
|
|
1778
|
+
letter-spacing: 0.14em;
|
|
1779
|
+
text-transform: uppercase;
|
|
1780
|
+
margin-bottom: 0.35rem;
|
|
1781
|
+
display: flex;
|
|
1782
|
+
justify-content: space-between;
|
|
1783
|
+
align-items: center;
|
|
1784
|
+
flex-wrap: wrap;
|
|
1785
|
+
gap: 0.4rem;
|
|
1786
|
+
}
|
|
1787
|
+
.data-value {
|
|
1788
|
+
color: var(--text-value);
|
|
1789
|
+
font-size: 0.82rem;
|
|
1790
|
+
word-break: break-all;
|
|
1791
|
+
background: var(--bg-value);
|
|
1792
|
+
padding: 0.65rem 0.85rem;
|
|
1793
|
+
border: 1px solid var(--border-value);
|
|
1794
|
+
box-shadow: inset 0 1px 3px rgba(0,0,0,0.5);
|
|
1795
|
+
border-radius: 2px;
|
|
1796
|
+
line-height: 1.45;
|
|
1797
|
+
}
|
|
1798
|
+
.data-value.cyan { color: var(--accent-cyan); border-color: rgba(34, 211, 238, 0.3); }
|
|
1799
|
+
.data-value.orange { color: var(--accent-orange); border-color: rgba(251, 146, 60, 0.3); }
|
|
1800
|
+
.data-value.green { color: var(--accent-green); border-color: rgba(74, 222, 128, 0.35); }
|
|
1801
|
+
.anchored-box {
|
|
1802
|
+
color: var(--accent-orange) !important;
|
|
1803
|
+
border: 1px solid var(--anchored-box-border) !important;
|
|
1804
|
+
background: var(--anchored-box-bg) !important;
|
|
1805
|
+
box-shadow: 0 0 18px rgba(251, 146, 60, 0.15), inset 0 1px 3px rgba(0,0,0,0.5) !important;
|
|
1806
|
+
font-weight: 600;
|
|
1807
|
+
}
|
|
1808
|
+
.anchored-badge {
|
|
1809
|
+
background: var(--anchored-badge-bg);
|
|
1810
|
+
color: var(--accent-orange);
|
|
1811
|
+
border: 1px solid var(--anchored-box-border);
|
|
1812
|
+
padding: 0.2rem 0.55rem;
|
|
1813
|
+
font-size: 0.62rem;
|
|
1814
|
+
font-weight: bold;
|
|
1815
|
+
letter-spacing: 0.12em;
|
|
1816
|
+
border-radius: 3px;
|
|
1817
|
+
}
|
|
1818
|
+
.solar-vault {
|
|
1819
|
+
display: flex;
|
|
1820
|
+
flex-direction: column;
|
|
1821
|
+
align-items: center;
|
|
1822
|
+
text-align: center;
|
|
1823
|
+
margin-bottom: 1.25rem;
|
|
1824
|
+
}
|
|
1825
|
+
.stamp-box {
|
|
1826
|
+
width: 240px;
|
|
1827
|
+
height: 240px;
|
|
1828
|
+
margin: 0.5rem auto 1rem auto;
|
|
1829
|
+
filter: var(--solar-shadow);
|
|
1830
|
+
max-width: 100%;
|
|
1831
|
+
}
|
|
1832
|
+
.stamp-box svg { width: 100%; height: 100%; }
|
|
1833
|
+
.grid-3 {
|
|
1834
|
+
display: grid;
|
|
1835
|
+
grid-template-columns: repeat(3, 1fr);
|
|
1836
|
+
gap: 1rem;
|
|
1837
|
+
margin-bottom: 1rem;
|
|
1838
|
+
}
|
|
1839
|
+
.grid-3:last-child { margin-bottom: 0; }
|
|
1840
|
+
@media (max-width: 640px) {
|
|
1841
|
+
body { padding: 0.5rem 0.35rem; }
|
|
1842
|
+
.monument { padding: 1.25rem 0.85rem; border-width: 1px; }
|
|
1843
|
+
.header-bar { flex-direction: column; align-items: flex-start; gap: 1rem; }
|
|
1844
|
+
.grid-3 { grid-template-columns: 1fr; gap: 0.75rem; }
|
|
1845
|
+
.stamp-box { width: 210px; height: 210px; }
|
|
1846
|
+
}
|
|
1847
|
+
@media print {
|
|
1848
|
+
body { background: #ffffff !important; color: #000000 !important; padding: 0 !important; }
|
|
1849
|
+
.theme-btn { display: none !important; }
|
|
1850
|
+
.monument { border: 1px solid #cbd5e1 !important; box-shadow: none !important; background: #ffffff !important; padding: 1.5rem !important; max-width: 100% !important; }
|
|
1851
|
+
.plate { background: #f8fafc !important; border: 1px solid #e2e8f0 !important; box-shadow: none !important; }
|
|
1852
|
+
.data-value { background: #ffffff !important; border: 1px solid #cbd5e1 !important; color: #000000 !important; }
|
|
1853
|
+
}
|
|
1854
|
+
</style>
|
|
1855
|
+
<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js"></script>
|
|
1856
|
+
</head>
|
|
1857
|
+
<body>
|
|
1858
|
+
<div class="monument">
|
|
1859
|
+
<div class="header-bar">
|
|
1860
|
+
<div class="brand-group">
|
|
1861
|
+
<div class="brand-logo">
|
|
1862
|
+
<span class="logo-bracket">[</span>
|
|
1863
|
+
<span class="logo-text">REL-TIME</span>
|
|
1864
|
+
<span class="logo-bracket">]</span>
|
|
1865
|
+
</div>
|
|
1866
|
+
<p class="brand-sub">SOVEREIGN CRYPTOGRAPHIC PROVENANCE CERTIFICATE</p>
|
|
1867
|
+
</div>
|
|
1868
|
+
<div class="header-actions">
|
|
1869
|
+
<div class="mode-pill">${modeLabel}</div>
|
|
1870
|
+
<button class="theme-btn" onclick="window.print()" aria-label="Print or Save as PDF">🖨️ PRINT / PDF</button>
|
|
1871
|
+
<button id="theme-toggle" class="theme-btn" onclick="toggleTheme()" aria-label="Toggle light or dark theme">☀️ LIGHT</button>
|
|
1872
|
+
</div>
|
|
1873
|
+
</div>
|
|
1874
|
+
|
|
1875
|
+
<div class="plate">
|
|
1876
|
+
<div class="plate-title">
|
|
1877
|
+
<span>Asset Identification</span>
|
|
1878
|
+
<span class="plate-subtitle">LOCAL ZERO-KNOWLEDGE SHA-256</span>
|
|
1879
|
+
</div>
|
|
1880
|
+
<div class="grid-3">
|
|
1881
|
+
<div class="data-row" style="margin:0;">
|
|
1882
|
+
<div class="data-label">File Name</div>
|
|
1883
|
+
<div class="data-value">${fileName}</div>
|
|
1884
|
+
</div>
|
|
1885
|
+
<div class="data-row" style="margin:0;">
|
|
1886
|
+
<div class="data-label">File Size</div>
|
|
1887
|
+
<div class="data-value">${fileSize}</div>
|
|
1888
|
+
</div>
|
|
1889
|
+
<div class="data-row" style="margin:0;">
|
|
1890
|
+
<div class="data-label">MIME Type</div>
|
|
1891
|
+
<div class="data-value">${mimeType}</div>
|
|
1892
|
+
</div>
|
|
1893
|
+
</div>
|
|
1894
|
+
</div>
|
|
1895
|
+
|
|
1896
|
+
<div class="plate">
|
|
1897
|
+
<div class="plate-title">
|
|
1898
|
+
<span>Plate I — Cryptographic Payload & Ownership</span>
|
|
1899
|
+
<span class="plate-subtitle" style="color:var(--accent-orange);">${
|
|
1900
|
+
signerPubKey ? "BIOMETRIC PASSKEY ATTESTED" : "ANONYMOUS PURITY"
|
|
1901
|
+
}</span>
|
|
1902
|
+
</div>
|
|
1903
|
+
<div class="data-row">
|
|
1904
|
+
<div class="data-label">[ THE WHAT ] ZERO-KNOWLEDGE FILE HASH (SHA-256)</div>
|
|
1905
|
+
<div class="data-value cyan">${originalHash}</div>
|
|
1906
|
+
</div>
|
|
1907
|
+
<div class="data-row">
|
|
1908
|
+
<div class="data-label">[ THE WHO ] SIGNATORY PUBLIC KEY</div>
|
|
1909
|
+
<div class="data-value cyan">${
|
|
1910
|
+
signerPubKey || "[ ANONYMOUS — RAW DATA INTEGRITY (NO PASSKEY) ]"
|
|
1911
|
+
}</div>
|
|
1912
|
+
</div>
|
|
1913
|
+
<div class="data-row">
|
|
1914
|
+
<div class="data-label">
|
|
1915
|
+
<span>ATTESTED IDENTITY HASH • GENERATES THE 64-RAY SUN-STAMP</span>
|
|
1916
|
+
<span class="anchored-badge">★ ANCHORED TO BITCOIN MERKLE TREE</span>
|
|
1917
|
+
</div>
|
|
1918
|
+
<div class="data-value anchored-box">${attestedHash}</div>
|
|
1919
|
+
</div>
|
|
1920
|
+
</div>
|
|
1921
|
+
|
|
1922
|
+
<div class="plate">
|
|
1923
|
+
<div class="plate-title">
|
|
1924
|
+
<span>Plate II — The Solar Seal & Thermodynamic Clock</span>
|
|
1925
|
+
<span class="plate-subtitle">SEEDED BY ATTESTED IDENTITY HASH</span>
|
|
1926
|
+
</div>
|
|
1927
|
+
<div class="solar-vault">
|
|
1928
|
+
<div class="stamp-box" id="stamp-mount"></div>
|
|
1929
|
+
</div>
|
|
1930
|
+
<div class="data-row">
|
|
1931
|
+
<div class="data-label">
|
|
1932
|
+
<span>[ THE WHEN ] THERMODYNAMIC PROOF (T_INDEX)</span>
|
|
1933
|
+
<span class="plate-subtitle">${isStandard ? "STATIC BLOCK SEED" : "VDF CLOCK SNAPSHOT"}</span>
|
|
1934
|
+
</div>
|
|
1935
|
+
<div class="data-value orange">${tIndex}</div>
|
|
1936
|
+
</div>
|
|
1937
|
+
<div class="data-row">
|
|
1938
|
+
<div class="data-label">SOLAR VDF TICKS ELAPSED SINCE LAST BITCOIN BLOCK</div>
|
|
1939
|
+
<div class="data-value orange" style="font-weight:bold;">${
|
|
1940
|
+
isStandard ? "0 (Bypassed — Standard Mode)" : formattedTicks
|
|
1941
|
+
}</div>
|
|
1942
|
+
</div>
|
|
1943
|
+
</div>
|
|
1944
|
+
|
|
1945
|
+
<div class="plate">
|
|
1946
|
+
<div class="plate-title">
|
|
1947
|
+
<span>Plate III — Bitcoin Timechain Finality</span>
|
|
1948
|
+
<span class="plate-subtitle" style="color:${
|
|
1949
|
+
isSettled ? "var(--accent-green)" : "var(--accent-orange)"
|
|
1950
|
+
};">${isSettled ? "MAINNET OP_RETURN SETTLEMENT" : "AWAITING BLOCK SETTLEMENT"}</span>
|
|
1951
|
+
</div>
|
|
1952
|
+
<div class="grid-3">
|
|
1953
|
+
<div class="data-row" style="margin:0;">
|
|
1954
|
+
<div class="data-label">Bitcoin Block Height</div>
|
|
1955
|
+
<div class="data-value ${isSettled ? "green" : "orange"}">${blockHeightDisplay}</div>
|
|
1956
|
+
</div>
|
|
1957
|
+
<div class="data-row" style="margin:0;">
|
|
1958
|
+
<div class="data-label">Block Timestamp (UTC)</div>
|
|
1959
|
+
<div class="data-value ${isSettled ? "green" : "orange"}">${blockTimeDisplay}</div>
|
|
1960
|
+
</div>
|
|
1961
|
+
<div class="data-row" style="margin:0;">
|
|
1962
|
+
<div class="data-label">Merkle Proof Path</div>
|
|
1963
|
+
<div class="data-value ${isSettled ? "green" : "orange"}">${merkleProofDisplay}</div>
|
|
1964
|
+
</div>
|
|
1965
|
+
</div>
|
|
1966
|
+
<div class="data-row">
|
|
1967
|
+
<div class="data-label">BITCOIN OP_RETURN TRANSACTION ID (TXID)</div>
|
|
1968
|
+
<div class="${finalityValueClass}">${txidDisplay}</div>
|
|
1969
|
+
</div>
|
|
1970
|
+
</div>
|
|
1971
|
+
</div>
|
|
1972
|
+
|
|
1973
|
+
<script>
|
|
1974
|
+
function toggleTheme() {
|
|
1975
|
+
const body = document.body;
|
|
1976
|
+
const btn = document.getElementById('theme-toggle');
|
|
1977
|
+
const isLight = body.classList.toggle('light-mode');
|
|
1978
|
+
if (btn) btn.innerHTML = isLight ? '🌙 DARK' : '☀️ LIGHT';
|
|
1979
|
+
try { localStorage.setItem('reltime_cert_theme', isLight ? 'light' : 'dark'); } catch(e) {}
|
|
1980
|
+
}
|
|
1981
|
+
try {
|
|
1982
|
+
if (localStorage.getItem('reltime_cert_theme') === 'light') {
|
|
1983
|
+
document.body.classList.add('light-mode');
|
|
1984
|
+
const btn = document.getElementById('theme-toggle');
|
|
1985
|
+
if (btn) btn.innerHTML = '🌙 DARK';
|
|
1986
|
+
}
|
|
1987
|
+
} catch(e) {}
|
|
1988
|
+
|
|
1989
|
+
function generateBWStamp() {
|
|
1990
|
+
const attestedHash = ${JSON.stringify(attestedHash)};
|
|
1991
|
+
const verifyUrl = 'https://rel-time.com/verify?t=' + attestedHash;
|
|
1992
|
+
const qr = qrcode(0, 'L');
|
|
1993
|
+
qr.addData(verifyUrl);
|
|
1994
|
+
qr.make();
|
|
1995
|
+
|
|
1996
|
+
const moduleCount = qr.getModuleCount();
|
|
1997
|
+
const quietZone = 3;
|
|
1998
|
+
const gridSize = 55;
|
|
1999
|
+
const offset = Math.floor((gridSize - moduleCount) / 2);
|
|
2000
|
+
const center = gridSize / 2;
|
|
2001
|
+
const cellSize = 3.85;
|
|
2002
|
+
const maxRadiusPx = 104.0;
|
|
2003
|
+
|
|
2004
|
+
let svgContent = '<svg viewBox="22 22 356 356" width="240" height="240" xmlns="http://www.w3.org/2000/svg">';
|
|
2005
|
+
svgContent += '<a href="' + verifyUrl + '" target="_blank" rel="noopener noreferrer" style="cursor:pointer;">';
|
|
2006
|
+
svgContent += '<circle cx="200" cy="200" r="109" fill="#ffffff" stroke="#000000" stroke-width="2" stroke-opacity="1"/>';
|
|
2007
|
+
|
|
2008
|
+
let qrDots = '';
|
|
2009
|
+
let noiseDots = '';
|
|
2010
|
+
|
|
2011
|
+
for (let r = 0; r < gridSize; r++) {
|
|
2012
|
+
for (let c = 0; c < gridSize; c++) {
|
|
2013
|
+
const dx = (c - center + 0.5) * cellSize;
|
|
2014
|
+
const dy = (r - center + 0.5) * cellSize;
|
|
2015
|
+
const distPx = Math.sqrt(dx * dx + dy * dy);
|
|
2016
|
+
const qrR = r - offset;
|
|
2017
|
+
const qrC = c - offset;
|
|
2018
|
+
const x = 200 + (c - center) * cellSize;
|
|
2019
|
+
const y = 200 + (r - center) * cellSize;
|
|
2020
|
+
|
|
2021
|
+
if (qrR >= 0 && qrR < moduleCount && qrC >= 0 && qrC < moduleCount) {
|
|
2022
|
+
if (qr.isDark(qrR, qrC)) {
|
|
2023
|
+
qrDots += '<rect x="' + x.toFixed(2) + '" y="' + y.toFixed(2) + '" width="' + (cellSize + 0.22).toFixed(2) + '" height="' + (cellSize + 0.22).toFixed(2) + '" />';
|
|
2024
|
+
}
|
|
2025
|
+
} else if (qrR >= -quietZone && qrR < moduleCount + quietZone && qrC >= -quietZone && qrC < moduleCount + quietZone) {
|
|
2026
|
+
} else if (distPx <= maxRadiusPx) {
|
|
2027
|
+
const seed = Math.sin(r * 12.9898 + c * 78.233) * 43758.5453;
|
|
2028
|
+
if ((seed - Math.floor(seed)) > 0.5) {
|
|
2029
|
+
noiseDots += '<rect x="' + x.toFixed(2) + '" y="' + y.toFixed(2) + '" width="' + (cellSize + 0.2).toFixed(2) + '" height="' + (cellSize + 0.2).toFixed(2) + '" />';
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
svgContent += '<g fill="#000000" transform="rotate(14 200 200)">' + qrDots + '</g>';
|
|
2036
|
+
svgContent += '<g fill="#000000" transform="rotate(14 200 200)">' + noiseDots + '</g>';
|
|
2037
|
+
|
|
2038
|
+
const hash = attestedHash;
|
|
2039
|
+
let radials = '<g>';
|
|
2040
|
+
for (let i = 0; i < 64; i++) {
|
|
2041
|
+
const val = parseInt(hash[i] || '0', 16) || 0;
|
|
2042
|
+
const r_length = 15 + (val / 15) * 45;
|
|
2043
|
+
const r_inner = 115;
|
|
2044
|
+
const a = (i * 360 / 64 - 90) * Math.PI / 180;
|
|
2045
|
+
const x1 = 200 + r_inner * Math.cos(a);
|
|
2046
|
+
const y1 = 200 + r_inner * Math.sin(a);
|
|
2047
|
+
const x2 = 200 + (r_inner + r_length) * Math.cos(a);
|
|
2048
|
+
const y2 = 200 + (r_inner + r_length) * Math.sin(a);
|
|
2049
|
+
const dynamicRayColor = (val % 2 === 1) ? '#facc15' : '#ea580c';
|
|
2050
|
+
radials += '<line x1="' + x1.toFixed(2) + '" y1="' + y1.toFixed(2) + '" x2="' + x2.toFixed(2) + '" y2="' + y2.toFixed(2) + '" stroke="' + dynamicRayColor + '" stroke-width="2.5" stroke-linecap="round" />';
|
|
2051
|
+
}
|
|
2052
|
+
radials += '</g></a></svg>';
|
|
2053
|
+
svgContent += radials;
|
|
2054
|
+
|
|
2055
|
+
const mount = document.getElementById('stamp-mount');
|
|
2056
|
+
if (mount) mount.innerHTML = svgContent;
|
|
2057
|
+
}
|
|
2058
|
+
generateBWStamp();
|
|
2059
|
+
</script>
|
|
2060
|
+
</body>
|
|
2061
|
+
</html>`;
|
|
2062
|
+
}
|
|
2063
|
+
|
|
1103
2064
|
const TOOLS = [
|
|
1104
2065
|
{
|
|
1105
2066
|
name: "reltime_anchor_file",
|
|
1106
2067
|
description:
|
|
1107
|
-
"Zero-knowledge file anchoring + 1-Click Stamped Copy: Computes the SHA-256 hash of a local file ENTIRELY ON THE LOCAL MACHINE (the file never leaves the user's computer), supports both Anonymous and Biometric (Passkey) Anchors, anchors the 64-char hash to the Bitcoin Timechain via Rel-Time, saves the 64-ray Sun-Stamp SVG
|
|
2068
|
+
"Zero-knowledge file anchoring + 1-Click Stamped Copy + 4-Plate Provenance Certificate: Computes the SHA-256 hash of a local file ENTIRELY ON THE LOCAL MACHINE (the file never leaves the user's computer), supports both Anonymous and Biometric (Passkey) Anchors, anchors the 64-char hash to the Bitcoin Timechain via Rel-Time, saves the 64-ray Sun-Stamp SVG, JSON Doomsday Proof, 4-Plate Sovereign Cryptographic Provenance Certificate (`reltime-certificate-<name>.html`), and automatically creates a Stamped Copy (`reltime-stamped-<name>.pdf` or `.png`) with the Sun-Stamp overlaid in the bottom-right corner for PDFs and Images.",
|
|
1108
2069
|
inputSchema: {
|
|
1109
2070
|
type: "object",
|
|
1110
2071
|
properties: {
|
|
@@ -1132,6 +2093,16 @@ const TOOLS = [
|
|
|
1132
2093
|
description:
|
|
1133
2094
|
"If true (default), automatically generates `reltime-stamped-<name>.pdf` or `.png` with the crisp 64-ray Sun-Stamp in the bottom-right corner when the file is a PDF or Image.",
|
|
1134
2095
|
},
|
|
2096
|
+
save_certificate_html: {
|
|
2097
|
+
type: "boolean",
|
|
2098
|
+
description:
|
|
2099
|
+
"If true (default), saves the 4-Plate Sovereign Cryptographic Provenance Certificate (`reltime-certificate-<filename>.html`) in the same folder as the file.",
|
|
2100
|
+
},
|
|
2101
|
+
open_certificate_in_browser: {
|
|
2102
|
+
type: "boolean",
|
|
2103
|
+
description:
|
|
2104
|
+
"If true, opens the generated 4-Plate Sovereign Cryptographic Provenance Certificate HTML in the system browser.",
|
|
2105
|
+
},
|
|
1135
2106
|
api_key: {
|
|
1136
2107
|
type: "string",
|
|
1137
2108
|
description: "Optional override for RELTIME_API_KEY (Autonomous Agent, Universal Anchor, or Enterprise Superhighway key).",
|
|
@@ -1228,7 +2199,7 @@ const TOOLS = [
|
|
|
1228
2199
|
{
|
|
1229
2200
|
name: "reltime_check_proof",
|
|
1230
2201
|
description:
|
|
1231
|
-
"Queries `GET /v1/proof/{hash}` to check the Bitcoin settlement status (Block Height, Block UTC Time, Bitcoin TXID, Merkle Root, and Merkle Inclusion Path) of an anchored hash. Optionally saves or updates the finalized `.json` Doomsday Proof receipt
|
|
2202
|
+
"Queries `GET /v1/proof/{hash}` to check the Bitcoin settlement status (Block Height, Block UTC Time, Bitcoin TXID, Merkle Root, and Merkle Inclusion Path) of an anchored hash. Optionally saves or updates the finalized `.json` Doomsday Proof receipt AND/OR generates the 4-Plate Sovereign Cryptographic Provenance Certificate HTML on demand.",
|
|
1232
2203
|
inputSchema: {
|
|
1233
2204
|
type: "object",
|
|
1234
2205
|
properties: {
|
|
@@ -1236,10 +2207,29 @@ const TOOLS = [
|
|
|
1236
2207
|
type: "string",
|
|
1237
2208
|
description: "The 64-character client_hash or t_index to verify.",
|
|
1238
2209
|
},
|
|
2210
|
+
file_path: {
|
|
2211
|
+
type: "string",
|
|
2212
|
+
description:
|
|
2213
|
+
"Optional local file path corresponding to the anchored hash (used to verify local custody/Passkey ownership and populate exact file name, size, and MIME type on the Provenance Certificate).",
|
|
2214
|
+
},
|
|
1239
2215
|
output_json_path: {
|
|
1240
2216
|
type: "string",
|
|
1241
2217
|
description: "Optional local file path to save the settled JSON Doomsday Proof receipt.",
|
|
1242
2218
|
},
|
|
2219
|
+
save_certificate_html: {
|
|
2220
|
+
type: "boolean",
|
|
2221
|
+
description:
|
|
2222
|
+
"If true, generates and saves the 4-Plate Sovereign Cryptographic Provenance Certificate (`reltime-certificate-<name>.html`) on demand.",
|
|
2223
|
+
},
|
|
2224
|
+
output_certificate_html_path: {
|
|
2225
|
+
type: "string",
|
|
2226
|
+
description: "Optional custom file path to save the 4-Plate Provenance Certificate HTML.",
|
|
2227
|
+
},
|
|
2228
|
+
open_certificate_in_browser: {
|
|
2229
|
+
type: "boolean",
|
|
2230
|
+
description:
|
|
2231
|
+
"If true, generates the 4-Plate Sovereign Cryptographic Provenance Certificate HTML and opens it in the system browser.",
|
|
2232
|
+
},
|
|
1243
2233
|
},
|
|
1244
2234
|
required: ["hash"],
|
|
1245
2235
|
},
|
|
@@ -1247,7 +2237,7 @@ const TOOLS = [
|
|
|
1247
2237
|
{
|
|
1248
2238
|
name: "reltime_account_status",
|
|
1249
2239
|
description:
|
|
1250
|
-
"Checks the remaining Hash Credits, tier, and lifetime usage of the configured Rel-Time API key (`GET /api/v1/account`). If no API key is configured yet, returns the 4 available Rel-Time Tiers (`universal` $3, `launch` $5/
|
|
2240
|
+
"Checks the remaining Hash Credits, tier, and lifetime usage of the configured Rel-Time API key (`GET /api/v1/account`). If no API key is configured yet, returns the 4 available Rel-Time Tiers (`universal` $3, `launch` $5/25, `standard` $50/500, `enterprise` $399/5,000) and payment options (`lightning` or `stripe`) so you can ask the user which tier they want to provision in chat.",
|
|
1251
2241
|
inputSchema: {
|
|
1252
2242
|
type: "object",
|
|
1253
2243
|
properties: {
|
|
@@ -1262,7 +2252,7 @@ const TOOLS = [
|
|
|
1262
2252
|
{
|
|
1263
2253
|
name: "reltime_provision_agent_key",
|
|
1264
2254
|
description:
|
|
1265
|
-
"Self-onboarding & Top-Up for all 4 Rel-Time Tiers via EITHER Bitcoin Lightning OR Stripe Card Checkout (`POST /v1/agent/invoice` & `POST /v1/agent/claim`). IMPORTANT:\n1. Before calling `create_invoice` for a new key, ask the user which tier AND payment method (`lightning` or `stripe`) they want:\n - **Universal Anchor (`pack: 'universal'`, $3 / anchor):** Priority 10-Minute Bitcoin Queue · Standard Mode (Biometric or Anonymous) · Supports `quantity` (default 1).\n - **Agent Launch Pack (`pack: 'launch'`, $5 /
|
|
2255
|
+
"Self-onboarding & Top-Up for all 4 Rel-Time Tiers via EITHER Bitcoin Lightning OR Stripe Card Checkout (`POST /v1/agent/invoice` & `POST /v1/agent/claim`). IMPORTANT:\n1. Before calling `create_invoice` for a new key, ask the user which tier AND payment method (`lightning` or `stripe`) they want:\n - **Universal Anchor (`pack: 'universal'`, $3 / anchor):** Priority 10-Minute Bitcoin Queue · Standard Mode (Biometric or Anonymous) · Supports `quantity` (default 1).\n - **Agent Launch Pack (`pack: 'launch'`, $5 / 25 Hashes, max 3 per key):** Priority 10-Minute Bitcoin Queue · Standard Mode · Includes Sun-Stamp, 1-Click Stamped Copy (.PDF/.PNG), 4-Plate Certificate, and .JSON Doomsday Proof.\n - **Autonomous Agents (`pack: 'standard'`, $50 / 500 Hashes):** Dynamic 6-Hour Batch Queue · Supports both Temporal Mode (`T_index` sub-second sequencing) and Standard Mode · Reloadable anytime (+500 credits / $50) with zero key rotation.\n - **Rel-Time Superhighway (`pack: 'enterprise'`, $399 / 5,000 Hashes):** Priority 10-Minute Bitcoin Queue · Supports Temporal Mode (`T_index`) & Standard Mode + Data Integrity Audit & Instant Tamper Detection.\n2. When `create_invoice` returns:\n - For **Lightning**: ALWAYS present BOTH the scannable QR code image (by calling Read on `qr_code_png_path`) AND the full, un-truncated `ln_invoice` (`bolt11`) string in a copyable code block on the VERY FIRST try!\n - For **Stripe**: The Stripe Checkout page opens automatically in the user's browser; also present the clickable `checkout_url` link and QR code (`qr_code_png_path`). Once paid, call `claim_invoice` with `invoice_id` (`cs_...`) to activate and save the key.",
|
|
1266
2256
|
inputSchema: {
|
|
1267
2257
|
type: "object",
|
|
1268
2258
|
properties: {
|
|
@@ -1276,7 +2266,7 @@ const TOOLS = [
|
|
|
1276
2266
|
type: "string",
|
|
1277
2267
|
enum: ["universal", "launch", "standard", "enterprise"],
|
|
1278
2268
|
description:
|
|
1279
|
-
"REQUIRED for 'create_invoice' after asking the user. Which tier the user selected: 'universal' ($3/anchor), 'launch' ($5 /
|
|
2269
|
+
"REQUIRED for 'create_invoice' after asking the user. Which tier the user selected: 'universal' ($3/anchor), 'launch' ($5 / 25 Hashes, max 3 per key), 'standard' ($50 / 500 Hashes), or 'enterprise' ($399 / 5,000 Hashes — Superhighway). If omitted on 'create_invoice', returns the 4 tier options so you can ask the user.",
|
|
1280
2270
|
},
|
|
1281
2271
|
payment_method: {
|
|
1282
2272
|
type: "string",
|
|
@@ -1311,7 +2301,7 @@ const TOOLS = [
|
|
|
1311
2301
|
{
|
|
1312
2302
|
name: "reltime_audit_trail",
|
|
1313
2303
|
description:
|
|
1314
|
-
"
|
|
2304
|
+
"SUPERHIGHWAY EXCLUSIVE ($399 / 5,000 Hashes): Data Integrity Audit, Instant Tamper Detection & Forensic Timechain Report (`GET /api/v1/account` + `GET /api/v1/account/history` + local SHA-256 cross-reference). Fetches the account's chronological anchor ledger, aggregates Bitcoin block settlements, VDF `T_index` ticks, and Corporate Hardware Passkey (`signer_public_key`) signatures, verifies a local file or entire folder bit-for-bit against the ledger (`verify_local_path`), and spins up a self-contained interactive HTML Forensic Audit Report (`reltime-audit-report-*.html`) in the user's browser.",
|
|
1315
2305
|
inputSchema: {
|
|
1316
2306
|
type: "object",
|
|
1317
2307
|
properties: {
|
|
@@ -1503,6 +2493,39 @@ async function executeTool(name, args = {}) {
|
|
|
1503
2493
|
stampedCopyResult = await stampLocalFileOptionA(filePath, svgContent, null, clientHash);
|
|
1504
2494
|
}
|
|
1505
2495
|
|
|
2496
|
+
// 4. Generate 4-Plate Sovereign Cryptographic Provenance Certificate HTML
|
|
2497
|
+
let savedCertificateHtmlPath = null;
|
|
2498
|
+
if (args.save_certificate_html !== false) {
|
|
2499
|
+
const certHtml = generateProvenanceCertificateHtml({
|
|
2500
|
+
fileName: path.basename(filePath),
|
|
2501
|
+
fileSize: formatBytes(fileBuffer.length),
|
|
2502
|
+
mimeType: inferMimeTypeFromFileName(path.basename(filePath)),
|
|
2503
|
+
originalFileHash,
|
|
2504
|
+
attestedHash: clientHash,
|
|
2505
|
+
signerPublicKey: passkeyCreds ? passkeyCreds.public_key_hex : null,
|
|
2506
|
+
anchorMode: data.anchor_mode || anchorMode,
|
|
2507
|
+
tIndex,
|
|
2508
|
+
vdfTicks: data.vdf_ticks || data.solar_ticks || 0,
|
|
2509
|
+
status: data.status || "pending_finality",
|
|
2510
|
+
blockHeight: data.settlement?.bitcoin_block_height || null,
|
|
2511
|
+
blockTimeUtc: data.settlement?.bitcoin_block_time
|
|
2512
|
+
? new Date(data.settlement.bitcoin_block_time * 1000).toISOString().replace("T", " ").slice(0, 19) + " UTC"
|
|
2513
|
+
: null,
|
|
2514
|
+
merkleProofCount: Array.isArray(data.settlement?.merkle_proof_path)
|
|
2515
|
+
? data.settlement.merkle_proof_path.length
|
|
2516
|
+
: null,
|
|
2517
|
+
txid: data.settlement?.bitcoin_txid || null,
|
|
2518
|
+
});
|
|
2519
|
+
savedCertificateHtmlPath = path.join(
|
|
2520
|
+
path.dirname(filePath),
|
|
2521
|
+
`reltime-certificate-${path.basename(filePath)}.html`
|
|
2522
|
+
);
|
|
2523
|
+
fs.writeFileSync(savedCertificateHtmlPath, certHtml, "utf8");
|
|
2524
|
+
if (args.open_certificate_in_browser) {
|
|
2525
|
+
openSystemBrowser(savedCertificateHtmlPath);
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
|
|
1506
2529
|
return {
|
|
1507
2530
|
status: data.status || "pending_finality",
|
|
1508
2531
|
file_name: path.basename(filePath),
|
|
@@ -1518,6 +2541,7 @@ async function executeTool(name, args = {}) {
|
|
|
1518
2541
|
sun_stamp_svg_url: stampEndpointUrl,
|
|
1519
2542
|
saved_sun_stamp_svg_path: savedSvgPath,
|
|
1520
2543
|
saved_doomsday_proof_json_path: savedProofJsonPath,
|
|
2544
|
+
saved_certificate_html_path: savedCertificateHtmlPath,
|
|
1521
2545
|
stamped_copy: stampedCopyResult,
|
|
1522
2546
|
zero_knowledge_guarantee:
|
|
1523
2547
|
"File was hashed and stamped 100% locally on your machine; 0 bytes of file content were transmitted.",
|
|
@@ -1635,6 +2659,81 @@ async function executeTool(name, args = {}) {
|
|
|
1635
2659
|
fs.writeFileSync(jsonPath, JSON.stringify(out, null, 2), "utf8");
|
|
1636
2660
|
out.saved_json_path = jsonPath;
|
|
1637
2661
|
}
|
|
2662
|
+
|
|
2663
|
+
const wantsCert =
|
|
2664
|
+
Boolean(args.save_certificate_html) ||
|
|
2665
|
+
Boolean(args.output_certificate_html_path) ||
|
|
2666
|
+
Boolean(args.open_certificate_in_browser);
|
|
2667
|
+
|
|
2668
|
+
if (wantsCert && res.ok) {
|
|
2669
|
+
let fileName = "Zero-Knowledge Hash Payload";
|
|
2670
|
+
let fileSize = "Zero-Knowledge (32-Byte Digest)";
|
|
2671
|
+
let mimeType = "application/octet-stream (Zero-Knowledge)";
|
|
2672
|
+
let originalFileHash = data.original_file_hash || data.client_hash || hash;
|
|
2673
|
+
let targetDir = os.tmpdir();
|
|
2674
|
+
|
|
2675
|
+
if (args.file_path) {
|
|
2676
|
+
const resolvedFile = resolveLocalFilePath(args.file_path);
|
|
2677
|
+
if (fs.existsSync(resolvedFile)) {
|
|
2678
|
+
const stat = fs.statSync(resolvedFile);
|
|
2679
|
+
fileName = path.basename(resolvedFile);
|
|
2680
|
+
fileSize = formatBytes(stat.size);
|
|
2681
|
+
mimeType = inferMimeTypeFromFileName(fileName);
|
|
2682
|
+
targetDir = path.dirname(resolvedFile);
|
|
2683
|
+
if (!data.original_file_hash) {
|
|
2684
|
+
originalFileHash = crypto
|
|
2685
|
+
.createHash("sha256")
|
|
2686
|
+
.update(fs.readFileSync(resolvedFile))
|
|
2687
|
+
.digest("hex");
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
const savedPasskey = loadSavedPasskey();
|
|
2693
|
+
if (data.signer_public_key) {
|
|
2694
|
+
out.passkey_custody_verified = Boolean(
|
|
2695
|
+
savedPasskey &&
|
|
2696
|
+
savedPasskey.public_key_hex.toLowerCase() === String(data.signer_public_key).toLowerCase()
|
|
2697
|
+
);
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
const certHtml = generateProvenanceCertificateHtml({
|
|
2701
|
+
fileName,
|
|
2702
|
+
fileSize,
|
|
2703
|
+
mimeType,
|
|
2704
|
+
originalFileHash,
|
|
2705
|
+
attestedHash: data.client_hash || hash,
|
|
2706
|
+
signerPublicKey: data.signer_public_key || null,
|
|
2707
|
+
anchorMode: data.anchor_mode || (data.vdf_ticks ? "temporal" : "standard"),
|
|
2708
|
+
tIndex: data.thermodynamic_proof || data.t_index || "PENDING VDF SNAPSHOT",
|
|
2709
|
+
vdfTicks: data.vdf_ticks || 0,
|
|
2710
|
+
status: data.status || "pending_finality",
|
|
2711
|
+
blockHeight: data.settlement?.bitcoin_block_height || null,
|
|
2712
|
+
blockTimeUtc: data.settlement?.bitcoin_block_time
|
|
2713
|
+
? new Date(data.settlement.bitcoin_block_time * 1000).toISOString().replace("T", " ").slice(0, 19) + " UTC"
|
|
2714
|
+
: null,
|
|
2715
|
+
merkleProofCount: Array.isArray(data.settlement?.merkle_proof_path)
|
|
2716
|
+
? data.settlement.merkle_proof_path.length
|
|
2717
|
+
: null,
|
|
2718
|
+
txid: data.settlement?.bitcoin_txid || null,
|
|
2719
|
+
});
|
|
2720
|
+
|
|
2721
|
+
const certPath = args.output_certificate_html_path
|
|
2722
|
+
? path.resolve(args.output_certificate_html_path)
|
|
2723
|
+
: path.join(
|
|
2724
|
+
targetDir,
|
|
2725
|
+
fileName !== "Zero-Knowledge Hash Payload"
|
|
2726
|
+
? `reltime-certificate-${fileName}.html`
|
|
2727
|
+
: `reltime-certificate-${hash.slice(0, 12)}.html`
|
|
2728
|
+
);
|
|
2729
|
+
fs.writeFileSync(certPath, certHtml, "utf8");
|
|
2730
|
+
out.saved_certificate_html_path = certPath;
|
|
2731
|
+
|
|
2732
|
+
if (args.open_certificate_in_browser) {
|
|
2733
|
+
openSystemBrowser(certPath);
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
|
|
1638
2737
|
return out;
|
|
1639
2738
|
}
|
|
1640
2739
|
|
|
@@ -1684,6 +2783,35 @@ async function executeTool(name, args = {}) {
|
|
|
1684
2783
|
throw new Error(`Rel-Time Account error (${acctRes.status}): ${await acctRes.text()}`);
|
|
1685
2784
|
}
|
|
1686
2785
|
const acctData = await acctRes.json();
|
|
2786
|
+
const tierName = String(acctData.tier || "");
|
|
2787
|
+
const isSuperhighway =
|
|
2788
|
+
tierName.includes("Superhighway") ||
|
|
2789
|
+
tierName.includes("Enterprise") ||
|
|
2790
|
+
key.startsWith("rel_key_enterprise_");
|
|
2791
|
+
|
|
2792
|
+
if (!isSuperhighway) {
|
|
2793
|
+
return {
|
|
2794
|
+
status: "superhighway_upgrade_required",
|
|
2795
|
+
current_tier: tierName || "Standard / Agent",
|
|
2796
|
+
hash_credits: Number(acctData.hash_credits || 0),
|
|
2797
|
+
message:
|
|
2798
|
+
"Data Integrity Audit, Instant Tamper Detection, and Forensic Timechain Reports (`reltime_audit_trail`) are exclusive to the Rel-Time Superhighway ($399 / 5,000 Hashes) tier.",
|
|
2799
|
+
how_to_upgrade:
|
|
2800
|
+
"Ask the user if they would like to upgrade their current key to Rel-Time Superhighway ($399 / 5,000 Hashes) via Bitcoin Lightning (`lightning`) or Stripe Card (`stripe`), then call `reltime_provision_agent_key({ action: 'create_invoice', pack: 'enterprise', payment_method, reload_existing: true })`.",
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
if (Number(acctData.hash_credits || 0) <= 0) {
|
|
2805
|
+
return {
|
|
2806
|
+
status: "superhighway_renewal_required",
|
|
2807
|
+
current_tier: tierName,
|
|
2808
|
+
hash_credits: 0,
|
|
2809
|
+
message:
|
|
2810
|
+
"Your Rel-Time Superhighway key currently has 0 active Hash Credits remaining. Please renew your Superhighway cycle ($399 / 5,000 Hashes) to run Data Integrity Audits & Forensic Reports.",
|
|
2811
|
+
how_to_renew:
|
|
2812
|
+
"Ask the user if they would like to renew their Superhighway key via Bitcoin Lightning (`lightning`) or Stripe Card (`stripe`), then call `reltime_provision_agent_key({ action: 'create_invoice', pack: 'enterprise', payment_method, reload_existing: true })`.",
|
|
2813
|
+
};
|
|
2814
|
+
}
|
|
1687
2815
|
|
|
1688
2816
|
const maxEntries = Math.min(500, Math.max(1, Number(args.limit) || 100));
|
|
1689
2817
|
const pageSize = Math.min(100, maxEntries);
|
|
@@ -2073,14 +3201,15 @@ async function executeTool(name, args = {}) {
|
|
|
2073
3201
|
|
|
2074
3202
|
if (!validPacks.includes(normalizedPack) || !validMethods.includes(normalizedMethod)) {
|
|
2075
3203
|
const missingNote = !validPacks.includes(normalizedPack)
|
|
2076
|
-
? "Please ask the user which Rel-Time Tier (`universal` $3, `launch` $5/
|
|
3204
|
+
? "Please ask the user which Rel-Time Tier (`universal` $3, `launch` $5/25, `standard` $50/500, or `enterprise` $399/5,000) AND which payment method (`lightning` or `stripe`) they would like to provision:"
|
|
2077
3205
|
: `The user selected pack '${normalizedPack}', but payment_method was not specified. Please ask the user whether they want to pay via Bitcoin Lightning ('lightning') or Stripe Card ('stripe') before calling create_invoice again:`;
|
|
2078
3206
|
return buildPackSelectionPrompt(missingNote);
|
|
2079
3207
|
}
|
|
2080
3208
|
|
|
2081
3209
|
const finalPack = normalizedPack === "superhighway" ? "enterprise" : normalizedPack;
|
|
2082
3210
|
const paymentMethod = normalizedMethod;
|
|
2083
|
-
const targetReloadKey =
|
|
3211
|
+
const targetReloadKey =
|
|
3212
|
+
args.api_key || ((args.reload_existing || finalPack === "launch") ? loadSavedApiKey() : "");
|
|
2084
3213
|
|
|
2085
3214
|
LAST_INVOICE_PACK = finalPack;
|
|
2086
3215
|
const reqBody = {
|
|
@@ -2183,7 +3312,8 @@ async function executeTool(name, args = {}) {
|
|
|
2183
3312
|
invoice_id: args.invoice_id,
|
|
2184
3313
|
pack: resolvedPack,
|
|
2185
3314
|
};
|
|
2186
|
-
const targetReloadKey =
|
|
3315
|
+
const targetReloadKey =
|
|
3316
|
+
args.api_key || ((args.reload_existing || resolvedPack === "launch") ? loadSavedApiKey() : "");
|
|
2187
3317
|
if (targetReloadKey && targetReloadKey.trim()) {
|
|
2188
3318
|
claimPayload.api_key = targetReloadKey.trim();
|
|
2189
3319
|
}
|