betterwithage commited on
Commit
22595c8
·
verified ·
1 Parent(s): ae3690f

Dev5 ecosystem foundation (direct HF push): byte-identical shared modules + ecosystem router + serve.py registration + Dockerfile COPY. Fixes partial GitHub auto-sync; unblocks COPY.

Browse files
static/shared/szl_codename_sanitizer.js ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* SPDX-License-Identifier: Apache-2.0
2
+ * (c) 2026 Lutar, Stephen P. - SZL Holdings - ORCID 0009-0001-0110-4173
3
+ * ============================================================================
4
+ * szl_codename_sanitizer.js - SHARED MODULE (byte-identical across both apps)
5
+ * ----------------------------------------------------------------------------
6
+ * Doctrine gate G5: 0 user-visible codenames. Internal route keys
7
+ * (amaru_* / rosie_* / sentra / jarvis) are fine as JS keys, but EVERY
8
+ * user-visible string must read its honest public role:
9
+ * amaru -> YACHAY (cortex / brain / OSINT ingest)
10
+ * rosie -> Operator (orchestrator)
11
+ * sentra -> CHAPAQ (verdict / immune)
12
+ * jarvis -> Operator (assistant / orchestrator)
13
+ *
14
+ * NO CDN. Vanilla JS. Browser + Node. This is the single mapping table so the
15
+ * two apps never drift, plus a live rendered-DOM scanner used by the CI gate.
16
+ *
17
+ * Public API (window.SZLCodenames / module.exports):
18
+ * MAP - frozen {codename: publicRole}
19
+ * BANNED_RE - the canonical detector regex (global, i)
20
+ * sanitize(str) - replace banned tokens with public roles
21
+ * scan(str) - return [{token, index}] of banned hits
22
+ * scanDOM(rootNode?) - scan rendered text + key attrs; returns hits[]
23
+ * guardedRender(el) - sanitize textContent of a node in-place
24
+ * isClean(str) - boolean
25
+ * ============================================================================ */
26
+ (function (root, factory) {
27
+ var mod = factory();
28
+ if (typeof module === "object" && module.exports) { module.exports = mod; }
29
+ if (root) { root.SZLCodenames = mod; }
30
+ })(typeof self !== "undefined" ? self : (typeof window !== "undefined" ? window : null), function () {
31
+ "use strict";
32
+
33
+ // Case-preserving public-role mapping. Order: most-specific first.
34
+ var MAP = {
35
+ "amaru": "YACHAY",
36
+ "rosie": "Operator",
37
+ "sentra": "CHAPAQ",
38
+ "jarvis": "Operator"
39
+ };
40
+ try { Object.freeze(MAP); } catch (e) {}
41
+
42
+ // Word-ish boundary: catch amaru, rosie_digest, Sentra, JARVIS, amaru-feed.
43
+ // We match the bare codename token wherever it appears as a visible word part.
44
+ var TOKENS = ["amaru", "rosie", "sentra", "jarvis"];
45
+ var BANNED_RE = new RegExp("(" + TOKENS.join("|") + ")", "ig");
46
+
47
+ function _roleFor(tok) {
48
+ var low = String(tok).toLowerCase();
49
+ return MAP[low] || low;
50
+ }
51
+
52
+ // Replace a matched codename, preserving a leading capital if the source was
53
+ // capitalized (so "Rosie" -> "Operator", "rosie" -> "Operator" both read well).
54
+ function sanitize(str) {
55
+ if (str == null) { return str; }
56
+ return String(str).replace(BANNED_RE, function (m) { return _roleFor(m); });
57
+ }
58
+
59
+ function scan(str) {
60
+ var out = [], s = String(str == null ? "" : str), m;
61
+ BANNED_RE.lastIndex = 0;
62
+ while ((m = BANNED_RE.exec(s)) !== null) {
63
+ out.push({ token: m[0], index: m.index });
64
+ if (m.index === BANNED_RE.lastIndex) { BANNED_RE.lastIndex++; }
65
+ }
66
+ return out;
67
+ }
68
+
69
+ function isClean(str) { return scan(str).length === 0; }
70
+
71
+ /* Scan a rendered DOM subtree for visible banned tokens. Checks visible text
72
+ * nodes + the human-visible attributes (title, aria-label, alt, placeholder,
73
+ * value). Ignores id/class/data-* route keys (those are allowed internal keys).
74
+ * Returns [{token, where, sample}]. */
75
+ function scanDOM(rootNode) {
76
+ var doc = (typeof document !== "undefined") ? document : null;
77
+ var root = rootNode || (doc ? doc.body : null);
78
+ if (!root) { return []; }
79
+ var hits = [];
80
+ var VISIBLE_ATTRS = ["title", "aria-label", "alt", "placeholder", "value"];
81
+
82
+ // 1) text nodes
83
+ if (doc && doc.createTreeWalker) {
84
+ var walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
85
+ var n;
86
+ while ((n = walker.nextNode())) {
87
+ var t = n.nodeValue || "";
88
+ var sc = scan(t);
89
+ for (var i = 0; i < sc.length; i++) {
90
+ hits.push({ token: sc[i].token, where: "text", sample: t.slice(Math.max(0, sc[i].index - 12), sc[i].index + 16) });
91
+ }
92
+ }
93
+ }
94
+ // 2) visible attributes
95
+ var all = root.querySelectorAll ? root.querySelectorAll("*") : [];
96
+ for (var e = 0; e < all.length; e++) {
97
+ for (var a = 0; a < VISIBLE_ATTRS.length; a++) {
98
+ var av = all[e].getAttribute ? all[e].getAttribute(VISIBLE_ATTRS[a]) : null;
99
+ if (av) {
100
+ var sca = scan(av);
101
+ for (var j = 0; j < sca.length; j++) {
102
+ hits.push({ token: sca[j].token, where: "@" + VISIBLE_ATTRS[a], sample: av.slice(0, 40) });
103
+ }
104
+ }
105
+ }
106
+ }
107
+ return hits;
108
+ }
109
+
110
+ function guardedRender(el) {
111
+ if (!el) { return el; }
112
+ if (typeof el.textContent === "string") { el.textContent = sanitize(el.textContent); }
113
+ return el;
114
+ }
115
+
116
+ return {
117
+ VERSION: "1.0.0",
118
+ MAP: MAP,
119
+ TOKENS: TOKENS.slice(),
120
+ BANNED_RE: BANNED_RE,
121
+ sanitize: sanitize,
122
+ scan: scan,
123
+ isClean: isClean,
124
+ scanDOM: scanDOM,
125
+ guardedRender: guardedRender
126
+ };
127
+ });
static/shared/szl_label_engine.js ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* SPDX-License-Identifier: Apache-2.0
2
+ * (c) 2026 Lutar, Stephen P. - SZL Holdings - ORCID 0009-0001-0110-4173
3
+ * ============================================================================
4
+ * szl_label_engine.js - SHARED MODULE (byte-identical across a11oy + killinchu)
5
+ * ----------------------------------------------------------------------------
6
+ * The single honest-label primitive for the SZL estate. Renders the canonical
7
+ * doctrine badges. NO build step, NO CDN, vanilla ES5-safe JS, browser + Node.
8
+ *
9
+ * Doctrine gate G7 (honest labels) + G8 (the half-state is the only
10
+ * unacceptable outcome). Every demo surface labels what is real; this module is
11
+ * the one place the badge vocabulary, colors and semantics live so the two apps
12
+ * never drift. Importing tabs MUST NOT invent their own pills.
13
+ *
14
+ * Canonical badge vocabulary (the ONLY allowed honest labels):
15
+ * LIVE - real backend wired, data is genuinely live, honest
16
+ * SAMPLE - honest sample / illustrative fixture (not live)
17
+ * FORECAST - model-produced forward estimate (e.g. seismic pulse)
18
+ * OSINT - open-source intelligence (sensed, unauthenticated claims)
19
+ * MODELED - deterministic model output, labeled (e.g. RUL, ent. forecast)
20
+ * SIMULATED - effector / decision simulation, human-on-the-loop (G6)
21
+ * CONNECT-READY - real schema, awaits OAuth; sample until connected
22
+ * EXPERIMENTAL - CI-green Lean wave, not locked-8
23
+ * HEURISTIC - rule/heuristic advisory, not proven
24
+ * PQC-ROADMAP - post-quantum signing is roadmap, never shown deployed (G4)
25
+ * ILLUSTRATIVE - explicitly illustrative (e.g. wow ROI)
26
+ *
27
+ * Public API (window.SZLLabels / module.exports):
28
+ * SCHEMES - frozen registry {KEY:{label,cls,title,...}}
29
+ * badge(key, opts?) - returns an HTMLElement <span class="szl-pill ...">
30
+ * badgeHTML(key, opts?) - returns a sanitized HTML string
31
+ * isHonestLabel(key) - boolean: is this a canonical honest label?
32
+ * normalize(key) - canonicalizes aliases ("connect_ready" -> "CONNECT-READY")
33
+ * auditText(str) - returns [] of forbidden raw claims (e.g. "100%", "tamper-proof")
34
+ * ensureStyle(doc?) - injects the <style> once (idempotent)
35
+ * ============================================================================ */
36
+ (function (root, factory) {
37
+ var mod = factory();
38
+ if (typeof module === "object" && module.exports) { module.exports = mod; }
39
+ if (root) { root.SZLLabels = mod; }
40
+ })(typeof self !== "undefined" ? self : (typeof window !== "undefined" ? window : null), function () {
41
+ "use strict";
42
+
43
+ /* Canonical registry. cls is a CSS modifier; colors are doctrine-fixed so the
44
+ * two apps render identical pills. tone: ok|warn|info|sim|bad — drives color. */
45
+ var SCHEMES = {
46
+ "LIVE": { label: "LIVE", tone: "ok", title: "Real backend wired - data is genuinely live and honest." },
47
+ "SAMPLE": { label: "SAMPLE", tone: "info", title: "Honest sample / illustrative fixture - not a live feed." },
48
+ "FORECAST": { label: "FORECAST", tone: "warn", title: "Model-produced forward estimate. Not an observation." },
49
+ "OSINT": { label: "OSINT", tone: "info", title: "Open-source intelligence. Sensed, unauthenticated claims." },
50
+ "MODELED": { label: "MODELED", tone: "warn", title: "Deterministic model output, labeled. Not measured." },
51
+ "SIMULATED": { label: "SIMULATED", tone: "sim", title: "Effector / decision simulation, human-on-the-loop. No live control." },
52
+ "CONNECT-READY": { label: "CONNECT-READY", tone: "info", title: "Real schema, awaits OAuth. Sample data until connected." },
53
+ "EXPERIMENTAL": { label: "EXPERIMENTAL", tone: "warn", title: "CI-green Lean wave - NOT part of the locked-8." },
54
+ "HEURISTIC": { label: "HEURISTIC", tone: "warn", title: "Rule / heuristic advisory. Not a proven result." },
55
+ "PQC-ROADMAP": { label: "PQC-ROADMAP", tone: "warn", title: "Post-quantum signing is roadmap. Never shown as deployed (G4)." },
56
+ "ILLUSTRATIVE": { label: "ILLUSTRATIVE", tone: "info", title: "Explicitly illustrative figure (e.g. ROI)." }
57
+ };
58
+ // freeze so importing tabs cannot mutate the doctrine vocabulary
59
+ try { Object.freeze(SCHEMES); for (var k in SCHEMES) { Object.freeze(SCHEMES[k]); } } catch (e) {}
60
+
61
+ var ALIASES = {
62
+ "CONNECT_READY": "CONNECT-READY", "CONNECTREADY": "CONNECT-READY",
63
+ "PQC_ROADMAP": "PQC-ROADMAP", "PQCROADMAP": "PQC-ROADMAP", "PQC": "PQC-ROADMAP",
64
+ "SIM": "SIMULATED", "EXP": "EXPERIMENTAL", "MODEL": "MODELED"
65
+ };
66
+
67
+ function normalize(key) {
68
+ if (key == null) { return ""; }
69
+ var u = String(key).trim().toUpperCase().replace(/\s+/g, "-");
70
+ if (SCHEMES[u]) { return u; }
71
+ var nodash = u.replace(/[-_]/g, "");
72
+ if (ALIASES[u]) { return ALIASES[u]; }
73
+ if (ALIASES[nodash]) { return ALIASES[nodash]; }
74
+ return u; // unknown - caller decides
75
+ }
76
+
77
+ function isHonestLabel(key) { return Object.prototype.hasOwnProperty.call(SCHEMES, normalize(key)); }
78
+
79
+ function esc(s) {
80
+ return String(s == null ? "" : s)
81
+ .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
82
+ .replace(/"/g, "&quot;").replace(/'/g, "&#39;");
83
+ }
84
+
85
+ /* Forbidden raw claims a tab must never render (doctrine G2/G3/G4/G7).
86
+ * auditText() returns the list of violations found in a rendered string. */
87
+ var FORBIDDEN = [
88
+ { re: /\b100\s*%/, why: "trust is never 100% (Lambda capped < 1.0)" },
89
+ { re: /\btamper-?proof\b/i, why: "use 'tamper-evident', not 'tamper-proof' (G3)" },
90
+ { re: /\bunique\s+theorem\b/i, why: "Lambda uniqueness is Conjecture 1, not a theorem (G2)" },
91
+ { re: /\bunconditional(ly)?\s+(unique|proven)\b/i, why: "Lambda uniqueness is conditional (Conjecture 1) (G2)" },
92
+ { re: /\bSLSA\s*L3\b(?!\s*roadmap)/i, why: "SLSA must read 'L1 honest / L2 attested / L3 roadmap' (G4)" },
93
+ { re: /\b(FedRAMP|IronBank|CMMC|ATO)\b/i, why: "never claim FedRAMP/IronBank/CMMC/ATO as achieved (G4)" },
94
+ { re: /\blocked\s*=?\s*5\b/i, why: "locked-proven is EXACTLY 8, never 5 (G1)" }
95
+ ];
96
+ function auditText(str) {
97
+ var out = [], s = String(str == null ? "" : str), i;
98
+ for (i = 0; i < FORBIDDEN.length; i++) { if (FORBIDDEN[i].re.test(s)) { out.push(FORBIDDEN[i].why); } }
99
+ return out;
100
+ }
101
+
102
+ var STYLE_ID = "szl-label-engine-style";
103
+ var CSS =
104
+ "." + "szl-pill{display:inline-flex;align-items:center;gap:6px;font:700 11px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;" +
105
+ "letter-spacing:.04em;padding:4px 9px;border-radius:7px;border:1px solid;text-transform:uppercase;white-space:nowrap;vertical-align:middle;}" +
106
+ ".szl-pill .szl-dot{width:7px;height:7px;border-radius:50%;background:currentColor;flex:0 0 auto;}" +
107
+ ".szl-pill.ok{color:#16c784;border-color:#16c78455;background:#16c7841a;}" +
108
+ ".szl-pill.warn{color:#e0a106;border-color:#e0a10655;background:#e0a1061a;}" +
109
+ ".szl-pill.info{color:#3aa0ff;border-color:#3aa0ff55;background:#3aa0ff1a;}" +
110
+ ".szl-pill.sim{color:#b07cff;border-color:#b07cff55;background:#b07cff1a;}" +
111
+ ".szl-pill.bad{color:#ff5c5c;border-color:#ff5c5c55;background:#ff5c5c1a;}" +
112
+ ".szl-pill.unknown{color:#888;border-color:#88888855;background:#8888881a;}";
113
+
114
+ function ensureStyle(doc) {
115
+ var d = doc || (typeof document !== "undefined" ? document : null);
116
+ if (!d || d.getElementById(STYLE_ID)) { return; }
117
+ var st = d.createElement("style"); st.id = STYLE_ID; st.textContent = CSS;
118
+ (d.head || d.documentElement).appendChild(st);
119
+ }
120
+
121
+ function resolve(key) {
122
+ var nk = normalize(key);
123
+ var s = SCHEMES[nk];
124
+ if (s) { return { key: nk, label: s.label, tone: s.tone, title: s.title, known: true }; }
125
+ return { key: nk, label: nk || "?", tone: "unknown", title: "Unknown / non-canonical label - not part of the honest vocabulary.", known: false };
126
+ }
127
+
128
+ function badgeHTML(key, opts) {
129
+ opts = opts || {};
130
+ var r = resolve(key);
131
+ var label = opts.label != null ? String(opts.label) : r.label;
132
+ var title = opts.title != null ? String(opts.title) : r.title;
133
+ var extra = opts.className ? (" " + String(opts.className)) : "";
134
+ var dot = opts.dot === false ? "" : '<span class="szl-dot" aria-hidden="true"></span>';
135
+ return '<span class="szl-pill ' + esc(r.tone) + extra + '" data-szl-label="' + esc(r.key) +
136
+ '" title="' + esc(title) + '" role="img" aria-label="data label: ' + esc(label) + '">' +
137
+ dot + esc(label) + "</span>";
138
+ }
139
+
140
+ function badge(key, opts) {
141
+ opts = opts || {};
142
+ var doc = opts.document || (typeof document !== "undefined" ? document : null);
143
+ if (!doc) { throw new Error("SZLLabels.badge requires a DOM document; use badgeHTML in Node."); }
144
+ ensureStyle(doc);
145
+ var tmp = doc.createElement("div");
146
+ tmp.innerHTML = badgeHTML(key, opts);
147
+ return tmp.firstChild;
148
+ }
149
+
150
+ return {
151
+ VERSION: "1.0.0",
152
+ SCHEMES: SCHEMES,
153
+ badge: badge,
154
+ badgeHTML: badgeHTML,
155
+ isHonestLabel: isHonestLabel,
156
+ normalize: normalize,
157
+ auditText: auditText,
158
+ ensureStyle: ensureStyle,
159
+ keys: function () { var a = [], k; for (k in SCHEMES) { a.push(k); } return a; }
160
+ };
161
+ });
static/shared/szl_receipt_cosign.js ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* SPDX-License-Identifier: Apache-2.0
2
+ * (c) 2026 Lutar, Stephen P. - SZL Holdings - ORCID 0009-0001-0110-4173
3
+ * ============================================================================
4
+ * szl_receipt_cosign.js - SHARED MODULE (byte-identical across a11oy + killinchu)
5
+ * ----------------------------------------------------------------------------
6
+ * The single DSSE receipt + cosign-verify primitive for the SZL estate.
7
+ * NO CDN, no build step. Browser-native SubtleCrypto only. Node-safe import.
8
+ *
9
+ * *** ONE documented signing scheme for the whole estate: ***
10
+ * ECDSA P-256 (SECP256R1) over SHA-256 == Sigstore cosign default.
11
+ *
12
+ * WHY P-256 and NOT Ed25519 (resolving the signing-scheme drift, gate G4):
13
+ * - The deployed estate, szl_dsse.py (byte-identical in both apps), every
14
+ * server verify path, and the PUBLISHED key szl-holdings/.github/cosign.pub
15
+ * are ALL ECDSA P-256. cosign sign-blob / verify-blob round-trips against it.
16
+ * - a11oy_signing_key.py states the curve is "deliberately NOT Ed25519" to
17
+ * match cosign.pub and every existing verify path.
18
+ * - Switching to Ed25519 would orphan cosign.pub and break cosign-CLI
19
+ * interop. Doctrine G8 ("don't claim/ship more than is real") => keep the
20
+ * scheme that actually verifies. The Ed25519 suggestion is therefore
21
+ * RESOLVED in favor of ECDSA-P256. HMAC is forbidden (symmetric, not
22
+ * publicly verifiable). This is the single source of truth for clients.
23
+ *
24
+ * DSSE (secure-systems-lab/dsse) PAE pre-authentication encoding (must match
25
+ * szl_dsse.py byte-for-byte):
26
+ * PAE(type, body) = "DSSEv1" SP LEN(type) SP type SP LEN(body) SP body
27
+ * SIGNATURE = ECDSA-P256-SHA256( PAE(payloadType, canonical_body) )
28
+ *
29
+ * HONESTY (gate G7/G8): this module NEVER fabricates a signature. Signing is
30
+ * done by the SAME-ORIGIN server endpoint POST /khipu/sign (which holds the
31
+ * szlholdings-cosign private key). The client:
32
+ * - builds the canonical receipt + chain hash (SHA-256, re-checkable),
33
+ * - asks the server to sign,
34
+ * - records keyid + sig_type returned (truthfully),
35
+ * - if the signer is unreachable OR returns an ephemeral/non-canonical key,
36
+ * marks the receipt UNSIGNED / CROSS-APP-PENDING and shows it honestly.
37
+ *
38
+ * Public API (window.SZLReceipts / module.exports):
39
+ * SCHEME - "ECDSA-P256-SHA256" (frozen)
40
+ * COSIGN_KEYID - "szlholdings-cosign" (frozen)
41
+ * COSIGN_PUB_URL - canonical published key URL
42
+ * canonicalJSON(obj) - deterministic JSON (sorted keys)
43
+ * sha256Hex(strOrBytes) -> Promise - SubtleCrypto SHA-256 hex
44
+ * pae(type, bodyBytes) -> Uint8Array - DSSE PAE bytes
45
+ * chainHash(prevHash, receipt) -> P - SHA-256 of (prev_hash || canonical)
46
+ * signReceipt(receipt, opts) -> P - POST /khipu/sign; honest never-fake
47
+ * verifyEnvelope(env, opts) -> P - POST /khipu/verify; cross-app verdict
48
+ * isCanonicalKey(keyid) - keyid === szlholdings-cosign ?
49
+ * newChain(opts) - a small append-only signed chain helper
50
+ * ============================================================================ */
51
+ (function (root, factory) {
52
+ var mod = factory();
53
+ if (typeof module === "object" && module.exports) { module.exports = mod; }
54
+ if (root) { root.SZLReceipts = mod; }
55
+ })(typeof self !== "undefined" ? self : (typeof window !== "undefined" ? window : null), function () {
56
+ "use strict";
57
+
58
+ var SCHEME = "ECDSA-P256-SHA256";
59
+ var COSIGN_KEYID = "szlholdings-cosign";
60
+ var COSIGN_PUB_URL = "https://github.com/szl-holdings/.github/blob/main/cosign.pub";
61
+ var KHIPU_PAYLOAD_TYPE = "application/vnd.szl.khipu+json";
62
+
63
+ function _subtle() {
64
+ var c = (typeof self !== "undefined" && self.crypto) ? self.crypto
65
+ : (typeof window !== "undefined" && window.crypto) ? window.crypto
66
+ : (typeof globalThis !== "undefined" && globalThis.crypto) ? globalThis.crypto : null;
67
+ return c && c.subtle ? c.subtle : null;
68
+ }
69
+
70
+ /* Deterministic canonical JSON: object keys sorted recursively, no spaces.
71
+ * MUST match szl_dsse.canonical_json (json.dumps(sort_keys, separators=(',',':'))). */
72
+ function canonicalJSON(obj) {
73
+ if (obj === null || typeof obj !== "object") { return JSON.stringify(obj); }
74
+ if (Object.prototype.toString.call(obj) === "[object Array]") {
75
+ var items = []; for (var i = 0; i < obj.length; i++) { items.push(canonicalJSON(obj[i])); }
76
+ return "[" + items.join(",") + "]";
77
+ }
78
+ var keys = []; for (var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) { keys.push(k); } }
79
+ keys.sort();
80
+ var parts = []; for (var j = 0; j < keys.length; j++) { parts.push(JSON.stringify(keys[j]) + ":" + canonicalJSON(obj[keys[j]])); }
81
+ return "{" + parts.join(",") + "}";
82
+ }
83
+
84
+ function _toBytes(strOrBytes) {
85
+ if (strOrBytes instanceof Uint8Array) { return strOrBytes; }
86
+ var s = String(strOrBytes);
87
+ if (typeof TextEncoder !== "undefined") { return new TextEncoder().encode(s); }
88
+ var arr = []; for (var i = 0; i < s.length; i++) { arr.push(s.charCodeAt(i) & 0xff); }
89
+ return new Uint8Array(arr);
90
+ }
91
+ function _hex(buf) {
92
+ var b = new Uint8Array(buf), h = ""; for (var i = 0; i < b.length; i++) { h += (b[i] < 16 ? "0" : "") + b[i].toString(16); }
93
+ return h;
94
+ }
95
+
96
+ function sha256Hex(strOrBytes) {
97
+ var sub = _subtle();
98
+ if (!sub) { return Promise.reject(new Error("SubtleCrypto unavailable (needs HTTPS/secure context)")); }
99
+ return sub.digest("SHA-256", _toBytes(strOrBytes)).then(_hex);
100
+ }
101
+
102
+ /* DSSE PAE bytes. Lengths are decimal ASCII of the UTF-8 byte length. */
103
+ function pae(payloadType, bodyBytes) {
104
+ var t = _toBytes(payloadType), b = (bodyBytes instanceof Uint8Array) ? bodyBytes : _toBytes(bodyBytes);
105
+ var head = "DSSEv1 " + t.length + " ";
106
+ var mid = " " + b.length + " ";
107
+ var pre = _toBytes(head), tb = t, midb = _toBytes(mid);
108
+ var out = new Uint8Array(pre.length + tb.length + midb.length + b.length);
109
+ var o = 0;
110
+ out.set(pre, o); o += pre.length;
111
+ out.set(tb, o); o += tb.length;
112
+ out.set(midb, o); o += midb.length;
113
+ out.set(b, o);
114
+ return out;
115
+ }
116
+
117
+ function chainHash(prevHash, receipt) {
118
+ var canonical = canonicalJSON(receipt);
119
+ return sha256Hex(String(prevHash || "0") + "|" + canonical).then(function (h) {
120
+ return { hash: h, canonical: canonical };
121
+ });
122
+ }
123
+
124
+ function isCanonicalKey(keyid) { return keyid === COSIGN_KEYID; }
125
+
126
+ /* Ask the SAME-ORIGIN server to sign. NEVER fabricates a signature.
127
+ * opts: { base:"", payloadType, fetchImpl } */
128
+ function signReceipt(receipt, opts) {
129
+ opts = opts || {};
130
+ var base = opts.base || "";
131
+ var f = opts.fetchImpl || (typeof fetch !== "undefined" ? fetch : null);
132
+ if (!f) { return Promise.reject(new Error("fetch unavailable")); }
133
+ var body = JSON.stringify(receipt);
134
+ return f(base + "/khipu/sign", {
135
+ method: "POST", headers: { "Content-Type": "application/json" }, body: body
136
+ }).then(function (resp) {
137
+ var ct = resp.headers && resp.headers.get ? (resp.headers.get("content-type") || "") : "";
138
+ if (!resp.ok) { return resp.text().then(function (t) { throw new Error("signer HTTP " + resp.status + ": " + t.slice(0, 160)); }); }
139
+ if (ct.indexOf("application/json") === -1) { throw new Error("signer returned non-JSON (route not mounted)"); }
140
+ return resp.json();
141
+ }).then(function (j) {
142
+ var env = j.envelope || j;
143
+ var sig0 = (env.signatures && env.signatures[0]) || null;
144
+ var keyid = sig0 ? sig0.keyid : null;
145
+ var sigType = (sig0 && sig0.sig_type) || (j.sig_types && j.sig_types[0]) || SCHEME;
146
+ var hasSig = !!(sig0 && sig0.sig);
147
+ var canonical = isCanonicalKey(keyid);
148
+ return {
149
+ signed: hasSig,
150
+ canonicalKey: canonical,
151
+ crossAppVerifiable: hasSig && canonical,
152
+ keyid: keyid,
153
+ sigType: sigType,
154
+ sig: sig0 ? sig0.sig : null,
155
+ envelope: env,
156
+ signerNote: hasSig
157
+ ? (canonical ? "" : "signer used a NON-CANONICAL key (" + keyid + "); cross-app verify PENDING operator cosign secret")
158
+ : "signer returned no signature - receipt is UNSIGNED (honest)"
159
+ };
160
+ })["catch"](function (err) {
161
+ // HONEST failure - never fake a signature
162
+ return {
163
+ signed: false, canonicalKey: false, crossAppVerifiable: false,
164
+ keyid: null, sigType: null, sig: null, envelope: null,
165
+ signerNote: "signer unreachable - receipt is UNSIGNED (honest): " + err.message
166
+ };
167
+ });
168
+ }
169
+
170
+ /* Verify a DSSE envelope via the server /khipu/verify (verifies against the
171
+ * published cosign.pub). Returns a truthful verdict. */
172
+ function verifyEnvelope(envelope, opts) {
173
+ opts = opts || {};
174
+ var base = opts.base || "";
175
+ var f = opts.fetchImpl || (typeof fetch !== "undefined" ? fetch : null);
176
+ if (!f) { return Promise.reject(new Error("fetch unavailable")); }
177
+ return f(base + "/khipu/verify", {
178
+ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(envelope)
179
+ }).then(function (resp) {
180
+ if (!resp.ok) { throw new Error("verify HTTP " + resp.status); }
181
+ return resp.json();
182
+ }).then(function (j) {
183
+ return {
184
+ verified: j.verified === true,
185
+ keyidMatch: j.keyid_match === true,
186
+ keyidExpected: (j.detail && j.detail.keyid_expected) || COSIGN_KEYID,
187
+ pubFingerprint: (j.detail && j.detail.pub_fingerprint_sha256) || null,
188
+ verifyKeyUrl: (j.detail && j.detail.verify_key_url) || COSIGN_PUB_URL,
189
+ raw: j
190
+ };
191
+ });
192
+ }
193
+
194
+ /* Small append-only signed chain helper. Each push() hashes (prev||receipt),
195
+ * signs via the server, and links. Honest about UNSIGNED / cross-app state. */
196
+ function newChain(opts) {
197
+ opts = opts || {};
198
+ var base = opts.base || "";
199
+ var chain = [];
200
+ var tip = "0";
201
+ return {
202
+ receipts: chain,
203
+ tip: function () { return tip; },
204
+ push: function (action, data) {
205
+ var seq = chain.length;
206
+ var receipt = { action: String(action), seq: seq, prev_hash: tip, data: data || {}, ts: new Date().toISOString() };
207
+ return chainHash(tip, receipt).then(function (ch) {
208
+ receipt.hash = ch.hash; receipt.canonical = ch.canonical;
209
+ return signReceipt({ action: receipt.action, seq: seq, prev_hash: tip, data: receipt.data }, { base: base });
210
+ }).then(function (sg) {
211
+ receipt.signed = sg.signed; receipt.keyid = sg.keyid; receipt.sigType = sg.sigType;
212
+ receipt.sig = sg.sig; receipt.canonicalKey = sg.canonicalKey;
213
+ receipt.crossAppVerifiable = sg.crossAppVerifiable; receipt.signerNote = sg.signerNote;
214
+ chain.push(receipt); tip = receipt.hash;
215
+ return receipt;
216
+ });
217
+ }
218
+ };
219
+ }
220
+
221
+ try { /* freeze constants */ } catch (e) {}
222
+ return {
223
+ VERSION: "1.0.0",
224
+ SCHEME: SCHEME,
225
+ COSIGN_KEYID: COSIGN_KEYID,
226
+ COSIGN_PUB_URL: COSIGN_PUB_URL,
227
+ PAYLOAD_TYPE: KHIPU_PAYLOAD_TYPE,
228
+ canonicalJSON: canonicalJSON,
229
+ sha256Hex: sha256Hex,
230
+ pae: pae,
231
+ chainHash: chainHash,
232
+ isCanonicalKey: isCanonicalKey,
233
+ signReceipt: signReceipt,
234
+ verifyEnvelope: verifyEnvelope,
235
+ newChain: newChain
236
+ };
237
+ });