Skip to content

Commit ac54f30

Browse files
authored
TRUTHFRAMER v1.7.0 — Public Independent Verification Console
Co-authored-by: midiakiasat <midiakiasat@users.noreply.github.com>
1 parent 98849a8 commit ac54f30

11 files changed

Lines changed: 885 additions & 3 deletions
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"protocol": "TRUTHFRAMER_PUBLIC_INDEPENDENT_VERIFICATION_WITNESS",
3+
"version": "v1.7.0",
4+
"status": "PUBLIC_INDEPENDENT_VERIFICATION_WITNESS_BOUND",
5+
"closure_claim": "A third party can verify TRUTHFRAMER public truth-frame continuity from public URLs only, without private source, local repository state, maintainer terminal output, or hidden context.",
6+
"public_console_path": "/verifier/",
7+
"public_verifier_script": "TRUTHFRAMER_PUBLIC_INDEPENDENT_VERIFIER.mjs",
8+
"public_checked_url_count": 2,
9+
"verifier": {
10+
"protocol": "TRUTHFRAMER_PUBLIC_INDEPENDENT_VERIFIER",
11+
"version": "v1.7.0",
12+
"status": "PUBLIC_INDEPENDENT_VERIFICATION_PASS",
13+
"public_inputs": {
14+
"public_verification_capsule_url": "https://truthframer.github.io/truthframer-platform/capsule/TRUTHFRAMER_PUBLIC_VERIFICATION_CAPSULE.json",
15+
"public_capsule_consumption_receipt_url": "https://truthframer.github.io/truthframer-platform/receipt/TRUTHFRAMER_PUBLIC_CAPSULE_CONSUMPTION_RECEIPT.json"
16+
},
17+
"public_object_digests": {
18+
"capsule_raw_bytes_sha256": "8f1b6dc9cf953c91f1f300ee28d4b66378059d44c464989d00aa385f3c3163e4",
19+
"capsule_canonical_json_sha256": "7d252db39d7f98ca1f239d3ecac2abaca0cfcad050c471e6efe1a1b7ec00f028",
20+
"receipt_raw_bytes_sha256": "94ee2f308b5a3c3415534b9439b0ad394dd338d41e8d6eb8eebb9563c7b1df48",
21+
"receipt_canonical_json_sha256": "ae2b0b1e7867d76ffb2df7ff97ebc6135e5b7b3eed5857b99a5d2161b75945f2"
22+
},
23+
"observed_public_claims": {
24+
"capsule_sha256": "957b92e87d6e6ca0f341702e367cb454347b66eba143e4046e3f0e4a8489fe17",
25+
"receipt_sha256": "ae2b0b1e7867d76ffb2df7ff97ebc6135e5b7b3eed5857b99a5d2161b75945f2",
26+
"receipt_sha256_basis": "computed_public_receipt_canonical_json_sha256",
27+
"receipt_capsule_sha256": "957b92e87d6e6ca0f341702e367cb454347b66eba143e4046e3f0e4a8489fe17",
28+
"capsule_fetched_from_public_url": true,
29+
"capsule_sha256_match": true,
30+
"capsule_sha256_match_basis": "expected_sha256_found_inside_public_capsule_json",
31+
"capsule_artifacts_live": true,
32+
"release_tag_bound": true,
33+
"no_private_source_required": true
34+
},
35+
"assertions": {
36+
"capsule_public_fetch_ok": true,
37+
"receipt_public_fetch_ok": true,
38+
"capsule_declares_sha256": true,
39+
"receipt_has_public_sha256_basis": true,
40+
"receipt_capsule_sha256_matches_capsule": true,
41+
"receipt_claims_capsule_public_fetch": true,
42+
"receipt_claims_capsule_sha256_match": true,
43+
"receipt_claims_capsule_artifacts_live": true,
44+
"receipt_claims_release_tag_bound": true,
45+
"receipt_claims_no_private_source_required": true,
46+
"receipt_sha_basis_is_public_capsule_internal_digest": true
47+
},
48+
"verifier_result_sha256": "3de5138348ac2a3227ec2f60c3c062afeddf209a837664b8881f7f020529b2f1"
49+
},
50+
"witness_sha256": "132a366bc7aee1df8fddd0ebf87fc2d21d107afd915b51a489aaed13c12c2323"
51+
}
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
const DEFAULTS = Object.freeze({
2+
version: "v1.7.0",
3+
capsuleUrl: "https://truthframer.github.io/truthframer-platform/capsule/TRUTHFRAMER_PUBLIC_VERIFICATION_CAPSULE.json",
4+
receiptUrl: "https://truthframer.github.io/truthframer-platform/receipt/TRUTHFRAMER_PUBLIC_CAPSULE_CONSUMPTION_RECEIPT.json"
5+
});
6+
7+
export function stableStringify(value) {
8+
if (value === null || typeof value !== "object") return JSON.stringify(value);
9+
if (Array.isArray(value)) return "[" + value.map(stableStringify).join(",") + "]";
10+
const keys = Object.keys(value).sort();
11+
return "{" + keys.map((key) => JSON.stringify(key) + ":" + stableStringify(value[key])).join(",") + "}";
12+
}
13+
14+
export async function sha256Hex(input) {
15+
const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
16+
17+
if (globalThis.crypto && globalThis.crypto.subtle) {
18+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
19+
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
20+
}
21+
22+
const { createHash } = await import("node:crypto");
23+
return createHash("sha256").update(Buffer.from(bytes)).digest("hex");
24+
}
25+
26+
function normalizeKey(key) {
27+
return String(key).replace(/[^a-zA-Z0-9]/g, "").toUpperCase();
28+
}
29+
30+
function isSha256(value) {
31+
return typeof value === "string" && /^[a-f0-9]{64}$/i.test(value);
32+
}
33+
34+
function asString(value) {
35+
if (value === null || value === undefined) return "";
36+
return String(value);
37+
}
38+
39+
function asBool(value) {
40+
if (value === true) return true;
41+
if (value === false) return false;
42+
if (typeof value === "string") return value.toLowerCase() === "true";
43+
return false;
44+
}
45+
46+
function walk(value, path = [], out = []) {
47+
if (!value || typeof value !== "object") return out;
48+
49+
if (Array.isArray(value)) {
50+
value.forEach((item, index) => walk(item, path.concat(String(index)), out));
51+
return out;
52+
}
53+
54+
for (const [key, child] of Object.entries(value)) {
55+
const itemPath = path.concat(key);
56+
out.push({
57+
key,
58+
normalizedKey: normalizeKey(key),
59+
normalizedPath: itemPath.map(normalizeKey).join("."),
60+
value: child
61+
});
62+
walk(child, itemPath, out);
63+
}
64+
65+
return out;
66+
}
67+
68+
function firstBoolBySemanticKey(root, names) {
69+
const wanted = new Set(names.map(normalizeKey));
70+
const hit = walk(root).find((entry) => wanted.has(entry.normalizedKey));
71+
return hit ? asBool(hit.value) : false;
72+
}
73+
74+
function firstStringBySemanticKey(root, names) {
75+
const wanted = new Set(names.map(normalizeKey));
76+
const hit = walk(root).find((entry) => wanted.has(entry.normalizedKey));
77+
return hit ? asString(hit.value) : "";
78+
}
79+
80+
function shaCandidates(root, predicate) {
81+
return walk(root)
82+
.filter((entry) => isSha256(entry.value))
83+
.filter(predicate)
84+
.map((entry) => ({
85+
key: entry.key,
86+
path: entry.normalizedPath,
87+
value: String(entry.value).toLowerCase()
88+
}));
89+
}
90+
91+
function preferredSha(root, predicate) {
92+
const hits = shaCandidates(root, predicate);
93+
if (!hits.length) return "";
94+
const exact = hits.find((hit) => hit.path.split(".").length <= 3);
95+
return (exact || hits[0]).value;
96+
}
97+
98+
function capsuleShaFromCapsule(capsuleJson) {
99+
return preferredSha(capsuleJson, (entry) => {
100+
const k = entry.normalizedKey;
101+
const p = entry.normalizedPath;
102+
return (
103+
k.endsWith("CAPSULESHA256") &&
104+
!p.includes("RAWBYTES") &&
105+
!p.includes("CANONICALJSON") &&
106+
!p.includes("WITHOUTSHAFIELDS") &&
107+
!p.includes("RECEIPT")
108+
);
109+
});
110+
}
111+
112+
function capsuleShaFromReceipt(receiptJson) {
113+
return preferredSha(receiptJson, (entry) => {
114+
const k = entry.normalizedKey;
115+
const p = entry.normalizedPath;
116+
return (
117+
k.endsWith("CAPSULESHA256") &&
118+
!p.includes("RAWBYTES") &&
119+
!p.includes("CANONICALJSON") &&
120+
!p.includes("WITHOUTSHAFIELDS") &&
121+
!p.includes("RECEIPT")
122+
);
123+
});
124+
}
125+
126+
function receiptShaFromReceipt(receiptJson) {
127+
return preferredSha(receiptJson, (entry) => {
128+
const k = entry.normalizedKey;
129+
const p = entry.normalizedPath;
130+
return (
131+
k.endsWith("RECEIPTSHA256") ||
132+
k.endsWith("CONSUMPTIONRECEIPTSHA256") ||
133+
p.endsWith("RECEIPTSHA256") ||
134+
p.endsWith("CONSUMPTIONRECEIPTSHA256")
135+
);
136+
});
137+
}
138+
139+
async function fetchPublicJson(url) {
140+
const response = await fetch(url, { cache: "no-store" });
141+
const text = await response.text();
142+
143+
if (!response.ok) {
144+
throw new Error(`PUBLIC_FETCH_FAILED url=${url} status=${response.status}`);
145+
}
146+
147+
return {
148+
url,
149+
status: response.status,
150+
text,
151+
json: JSON.parse(text),
152+
raw_bytes_sha256: await sha256Hex(text),
153+
canonical_json_sha256: await sha256Hex(stableStringify(JSON.parse(text)))
154+
};
155+
}
156+
157+
export async function verifyTruthframerPublicIndependently(config = {}) {
158+
const capsuleUrl = config.capsuleUrl || DEFAULTS.capsuleUrl;
159+
const receiptUrl = config.receiptUrl || DEFAULTS.receiptUrl;
160+
161+
const capsule = await fetchPublicJson(capsuleUrl);
162+
const receipt = await fetchPublicJson(receiptUrl);
163+
164+
const capsuleDeclaredSha = capsuleShaFromCapsule(capsule.json);
165+
const receiptCapsuleSha = capsuleShaFromReceipt(receipt.json);
166+
const receiptDeclaredSha = receiptShaFromReceipt(receipt.json);
167+
const receiptObservedSha = receiptDeclaredSha || receipt.canonical_json_sha256;
168+
169+
const claims = {
170+
capsule_sha256: capsuleDeclaredSha,
171+
receipt_sha256: receiptObservedSha,
172+
receipt_sha256_basis: receiptDeclaredSha ? "declared_inside_public_receipt_json" : "computed_public_receipt_canonical_json_sha256",
173+
receipt_capsule_sha256: receiptCapsuleSha,
174+
capsule_fetched_from_public_url: firstBoolBySemanticKey(receipt.json, ["CAPSULE_FETCHED_FROM_PUBLIC_URL"]),
175+
capsule_sha256_match: firstBoolBySemanticKey(receipt.json, ["CAPSULE_SHA256_MATCH"]),
176+
capsule_sha256_match_basis: firstStringBySemanticKey(receipt.json, ["CAPSULE_SHA256_MATCH_BASIS"]),
177+
capsule_artifacts_live: firstBoolBySemanticKey(receipt.json, ["CAPSULE_ARTIFACTS_LIVE"]),
178+
release_tag_bound: firstBoolBySemanticKey(receipt.json, ["RELEASE_TAG_BOUND"]),
179+
no_private_source_required: firstBoolBySemanticKey(receipt.json, ["NO_PRIVATE_SOURCE_REQUIRED"])
180+
};
181+
182+
const assertions = {
183+
capsule_public_fetch_ok: capsule.status === 200,
184+
receipt_public_fetch_ok: receipt.status === 200,
185+
capsule_declares_sha256: isSha256(claims.capsule_sha256),
186+
receipt_has_public_sha256_basis: isSha256(claims.receipt_sha256),
187+
receipt_capsule_sha256_matches_capsule: claims.receipt_capsule_sha256 === claims.capsule_sha256,
188+
receipt_claims_capsule_public_fetch: claims.capsule_fetched_from_public_url === true,
189+
receipt_claims_capsule_sha256_match: claims.capsule_sha256_match === true,
190+
receipt_claims_capsule_artifacts_live: claims.capsule_artifacts_live === true,
191+
receipt_claims_release_tag_bound: claims.release_tag_bound === true,
192+
receipt_claims_no_private_source_required: claims.no_private_source_required === true,
193+
receipt_sha_basis_is_public_capsule_internal_digest:
194+
claims.capsule_sha256_match_basis === "expected_sha256_found_inside_public_capsule_json"
195+
};
196+
197+
const pass = Object.values(assertions).every(Boolean);
198+
199+
const result = {
200+
protocol: "TRUTHFRAMER_PUBLIC_INDEPENDENT_VERIFIER",
201+
version: DEFAULTS.version,
202+
status: pass ? "PUBLIC_INDEPENDENT_VERIFICATION_PASS" : "PUBLIC_INDEPENDENT_VERIFICATION_FAIL",
203+
public_inputs: {
204+
public_verification_capsule_url: capsuleUrl,
205+
public_capsule_consumption_receipt_url: receiptUrl
206+
},
207+
public_object_digests: {
208+
capsule_raw_bytes_sha256: capsule.raw_bytes_sha256,
209+
capsule_canonical_json_sha256: capsule.canonical_json_sha256,
210+
receipt_raw_bytes_sha256: receipt.raw_bytes_sha256,
211+
receipt_canonical_json_sha256: receipt.canonical_json_sha256
212+
},
213+
observed_public_claims: claims,
214+
assertions
215+
};
216+
217+
result.verifier_result_sha256 = await sha256Hex(stableStringify(result));
218+
return result;
219+
}
220+
221+
if (typeof window !== "undefined") {
222+
window.TRUTHFRAMER_PUBLIC_INDEPENDENT_VERIFIER = {
223+
verifyTruthframerPublicIndependently,
224+
stableStringify,
225+
sha256Hex
226+
};
227+
}

docs/verifier/index.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>TRUTHFRAMER Public Independent Verification Console</title>
6+
<meta name="viewport" content="width=device-width,initial-scale=1">
7+
<style>
8+
:root { color-scheme: dark; background:#050505; color:#d8d8d8; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
9+
body { margin:0; padding:32px; }
10+
main { max-width:1120px; margin:0 auto; }
11+
h1 { font-size:18px; font-weight:600; letter-spacing:.04em; color:#f2f2f2; }
12+
p { color:#9a9a9a; line-height:1.5; max-width:880px; }
13+
pre { white-space:pre-wrap; word-break:break-word; background:#0b0b0b; border:1px solid #242424; padding:20px; border-radius:10px; }
14+
.pass { color:#8ff0a4; }
15+
.fail { color:#ff9b9b; }
16+
</style>
17+
</head>
18+
<body>
19+
<main>
20+
<h1>TRUTHFRAMER Public Independent Verification Console</h1>
21+
<p>
22+
This page verifies the public capsule and public consumption receipt from public URLs only.
23+
No private source, local repository state, maintainer terminal output, or hidden context is required.
24+
</p>
25+
<p id="status">VERIFYING...</p>
26+
<pre id="output"></pre>
27+
</main>
28+
<script type="module">
29+
import { verifyTruthframerPublicIndependently } from "./TRUTHFRAMER_PUBLIC_INDEPENDENT_VERIFIER.mjs";
30+
31+
const status = document.getElementById("status");
32+
const output = document.getElementById("output");
33+
34+
try {
35+
const result = await verifyTruthframerPublicIndependently();
36+
status.textContent = result.status;
37+
status.className = result.status.endsWith("_PASS") ? "pass" : "fail";
38+
output.textContent = JSON.stringify(result, null, 2);
39+
} catch (error) {
40+
status.textContent = "PUBLIC_INDEPENDENT_VERIFICATION_ERROR";
41+
status.className = "fail";
42+
output.textContent = String(error && error.stack ? error.stack : error);
43+
}
44+
</script>
45+
</body>
46+
</html>

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"name": "truthframer-platform",
3-
"version": "1.6.0",
3+
"version": "1.7.0",
44
"private": true,
55
"description": "The object spine for TRUTHFRAMER: replayable visual truth frames for market reality.",
66
"scripts": {
77
"test": "node scripts/verify-truthframer.js",
88
"verify": "node scripts/verify-truthframer.js",
99
"verify:render": "node scripts/verify-render.js",
1010
"verify:public": "node scripts/verify-public-surface.js",
11-
"verify:all": "npm run verify && npm run verify:render && npm run verify:public && npm run verify:registry && npm run verify:readme && npm run verify:tf000002 && npm run verify:tf000003 && npm run verify:tf000004 && npm run verify:root && npm run verify:audit && npm run verify:hardening && npm run verify:network-seal && npm run verify:verification-index && npm run verify:index-seal && npm run verify:verification-atlas && npm run verify:atlas-seal && npm run verify:stack-closure && npm run verify:stack-closure-seal && npm run verify:continuity-sentinel && npm run verify:continuity-sentinel-seal && npm run verify:release-closure && npm run verify:release-closure-seal && npm run verify:root-finality && npm run verify:root-finality-seal && npm run verify:privacy-perimeter && npm run verify:legal-privacy-perimeter && npm run verify:legal-privacy-perimeter-seal && npm run verify:legal-privacy-network && npm run verify:distribution-egress && npm run verify:distribution-egress-firewall && npm run verify:distribution-egress-firewall-seal && npm run verify:distribution-egress-network && npm run verify:source-provenance && npm run verify:source-provenance-seal && npm run verify:source-provenance-network && npm run verify:verification-purity && npm run verify:verification-immutability && npm run verify:verification-immutability-seal && npm run verify:verification-immutability-network && npm run verify:truth-frame-spine && npm run verify:truth-frame-spine-network && npm run verify:truth-frame-admission && npm run verify:truth-frame-admission-network && npm run verify:truth-frame-admission-refusal && npm run verify:truth-frame-admission-refusal-network && npm run verify:truth-frame-admission-closure && npm run verify:truth-frame-admission-closure-network && npm run verify:public-verification-capsule && npm run verify:public-verification-capsule-network && npm run verify:public-capsule-consumption-receipt && npm run verify:public-capsule-consumption-receipt-network",
11+
"verify:all": "npm run verify && npm run verify:render && npm run verify:public && npm run verify:registry && npm run verify:readme && npm run verify:tf000002 && npm run verify:tf000003 && npm run verify:tf000004 && npm run verify:root && npm run verify:audit && npm run verify:hardening && npm run verify:network-seal && npm run verify:verification-index && npm run verify:index-seal && npm run verify:verification-atlas && npm run verify:atlas-seal && npm run verify:stack-closure && npm run verify:stack-closure-seal && npm run verify:continuity-sentinel && npm run verify:continuity-sentinel-seal && npm run verify:release-closure && npm run verify:release-closure-seal && npm run verify:root-finality && npm run verify:root-finality-seal && npm run verify:privacy-perimeter && npm run verify:legal-privacy-perimeter && npm run verify:legal-privacy-perimeter-seal && npm run verify:legal-privacy-network && npm run verify:distribution-egress && npm run verify:distribution-egress-firewall && npm run verify:distribution-egress-firewall-seal && npm run verify:distribution-egress-network && npm run verify:source-provenance && npm run verify:source-provenance-seal && npm run verify:source-provenance-network && npm run verify:verification-purity && npm run verify:verification-immutability && npm run verify:verification-immutability-seal && npm run verify:verification-immutability-network && npm run verify:truth-frame-spine && npm run verify:truth-frame-spine-network && npm run verify:truth-frame-admission && npm run verify:truth-frame-admission-network && npm run verify:truth-frame-admission-refusal && npm run verify:truth-frame-admission-refusal-network && npm run verify:truth-frame-admission-closure && npm run verify:truth-frame-admission-closure-network && npm run verify:public-verification-capsule && npm run verify:public-verification-capsule-network && npm run verify:public-capsule-consumption-receipt && npm run verify:public-capsule-consumption-receipt-network && npm run verify:public-independent-verification-witness",
1212
"verify:registry": "node scripts/verify-registry.js",
1313
"verify:readme": "node scripts/verify-readme-public-entry.js",
1414
"verify:tf000002": "node scripts/verify-tf-000002.js",
@@ -72,7 +72,10 @@
7272
"verify:public-verification-capsule-network": "node scripts/verify-public-verification-capsule-network.js",
7373
"generate:public-capsule-consumption-receipt": "node scripts/generate-public-capsule-consumption-receipt.js",
7474
"verify:public-capsule-consumption-receipt": "node scripts/verify-public-capsule-consumption-receipt.js",
75-
"verify:public-capsule-consumption-receipt-network": "node scripts/verify-public-capsule-consumption-receipt-network.js"
75+
"verify:public-capsule-consumption-receipt-network": "node scripts/verify-public-capsule-consumption-receipt-network.js",
76+
"generate:public-independent-verification-witness": "node scripts/generate-public-independent-verification-witness.js",
77+
"verify:public-independent-verification-witness": "node scripts/verify-public-independent-verification-witness.js",
78+
"verify:public-independent-verification-witness-network": "node scripts/verify-public-independent-verification-witness-network.js"
7679
},
7780
"license": "UNLICENSED"
7881
}

0 commit comments

Comments
 (0)