apex-psi-verifier 0.0.0-stage → 1.2.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,45 @@
1
+ # Changelog — @apex/psi-verifier / psi-verifier (Python)
2
+
3
+ All notable changes to the MIT-licensed PSI verifier. Verification is, and
4
+ remains, free forever.
5
+
6
+ ## 1.1.1 — Parity release
7
+
8
+ - Strict cross-language parity: the TypeScript and Python distributions emit
9
+ **byte-identical** rejection text for the same input, with identical casing
10
+ and punctuation.
11
+ - Every finding carries a readable rule citation, e.g.
12
+ `PSI-SEAL v1.1.1 REJECT: schema_digest mismatch — rule 4.2`.
13
+ - Added `PSI_VERIFIER_VERSION`, `PSI_RULE_CITATIONS`, `rejectLine()` /
14
+ `reject_line()` and `formatAcceptance()` / `format_acceptance()`.
15
+ - Schema digest and Merkle root are unchanged and identical across both
16
+ distributions: `6d8d65e5fec9f58d762058eb8d47308e33a9e67c396a96ee8bdd84b14f4e04b9`.
17
+ - Every rejection closes with: *verified, not asserted; trust the math, not the maker.*
18
+
19
+ ## 1.1.0 — Enforcement release
20
+
21
+ **Intentional breaking change: enforcement is now the default.**
22
+
23
+ - `verify(seal)` (JS/TS) and `verify(seal)` (Python) **reject** seals that are
24
+ not PSI-SEAL/1 conformant, by throwing `PsiSealInvalidError`. Previously the
25
+ verifier only reported failures, so pipelines could accept non-conformant
26
+ data by accident.
27
+ - Rejection messages are actionable and stable: they print the canonical schema
28
+ digest, every failed normative rule, the canonical schema URL, and where to
29
+ generate a conformant seal.
30
+ - Legacy override: `verify(seal, { enforce: false })` / `verify(seal, enforce=False)`.
31
+ - Added `assertSeal()`, `isConformant()` / `is_conformant()`, `formatRejection()` /
32
+ `format_rejection()`, `PSI_SEAL_URL`, `PSI_SCHEMA_URL`.
33
+ - `verifySeal()` / `verify_seal()` are unchanged and never raise — use them for
34
+ reporting surfaces such as dashboards.
35
+ - New Python distribution (`psi-verifier`), byte-identical rule set and digest
36
+ to the TypeScript implementation.
37
+
38
+ Migration: if a pipeline previously ignored `result.conformant`, it will now
39
+ fail on non-conformant input. That is the intended behaviour — either seal the
40
+ data conformantly, or pass `enforce: false` explicitly.
41
+
42
+ ## 1.0.0 — Initial release
43
+
44
+ - PSI-SEAL/1.0.0 canonical schema verification (rules R1–R12), zero
45
+ dependencies, browser / Node 18+ / Deno / Bun / workers.
package/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 APEX Intelligence Empire / ROCKYFILMS888 PTY LTD (ABN 71 672 237 795), Victoria, Australia.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ NOTE ON SCOPE: this MIT grant covers the VERIFIER software only. The
24
+ PSI-SEAL/1 canonical seal schema is a separately copyrighted technical
25
+ specification, and the APEX PSI Sealing Engine (generator) is licensed
26
+ separately. See LICENSE-ENGINE.txt.
package/README.md CHANGED
@@ -1,3 +1,64 @@
1
- # Temporary Holding Version
1
+ # apex-psi-verifier
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > Apex PSI is not a tool and not a product. It is the verification substrate — the layer beneath proof. It does not judge. It does not certify. It only remembers.
4
+
5
+ **MIT. Free forever.** Verify any APEX PSI seal, anywhere — browser, Node, Deno, Bun, workers, CI. Zero dependencies.
6
+
7
+ ```ts
8
+ import { verifySeal } from "apex-psi-verifier";
9
+
10
+ const result = await verifySeal(JSON.parse(receiptJson));
11
+ // { conformant: true, schema_id: "PSI-SEAL/1.0.0", schema_digest_match: true,
12
+ // seal_hash_match: true, failures: [] }
13
+ ```
14
+
15
+ ## What it checks
16
+
17
+ The verifier enforces the **PSI-SEAL/1** canonical schema byte for byte:
18
+
19
+ | Rule | Check |
20
+ | --- | --- |
21
+ | R2 / R3 | Closed field set, canonical field order |
22
+ | R4 / R5 | 64-char lowercase hex SHA-256, unprefixed, no padding |
23
+ | R6 | `sealed_at` RFC 3339 UTC, exactly 3 fractional digits |
24
+ | R7 | Exact octet length, NFC-normalised subject name |
25
+ | R8 / R9 | Domain-separated leaf `SHA-256("PSI1:" ‖ hash)`, promoted-odd Merkle |
26
+ | R10 | `seal_hash = SHA-256(JCS(core envelope))` |
27
+ | R12 | `schema` + `schema_digest` present and matching |
28
+
29
+ Anything that deviates — even if functionally similar — fails. **Only schema-conformant seals are PSI-compliant.**
30
+
31
+ ## Licensing model
32
+
33
+ - **This verifier: MIT.** Check seals forever, at no cost, with no permission.
34
+ - **The generator (sealing engine): proprietary.** Free for personal / non-commercial use; commercial and institutional sealing is also free of charge in perpetuity (royalty tiers withdrawn 4 September 2026). See <https://ai-governance-standard.com/license>.
35
+
36
+ Schema: © 2026 APEX Infrastructure. Specification: <https://ai-governance-standard.com/spec>
37
+
38
+ ## v1.2.0 — enforcement by default, byte-identical across languages
39
+
40
+ `verify()` **rejects** non-conformant seals instead of merely reporting them, so
41
+ no pipeline accepts malformed evidence by accident:
42
+
43
+ ```ts
44
+ import { verify, PsiSealInvalidError } from "apex-psi-verifier";
45
+
46
+ try {
47
+ await verify(seal); // enforce: true by default
48
+ } catch (e) {
49
+ if (e instanceof PsiSealInvalidError) console.error(e.message);
50
+ }
51
+
52
+ await verify(seal, { enforce: false }); // legacy data only
53
+ ```
54
+
55
+ The rejection prints the canonical schema digest, every failed normative rule,
56
+ the canonical schema URL, and where a conformant seal is produced. Report-only
57
+ APIs remain: `verifySeal()` (never throws), `isConformant()`.
58
+
59
+ Rejection text is byte-identical in TypeScript and Python, and every line cites a
60
+ rule: `PSI-SEAL v1.2.0 REJECT: schema_digest mismatch — rule 4.2`. Free forever.
61
+ MIT. No permission required.
62
+
63
+ A byte-identical Python distribution ships as `psi-verifier`
64
+ (`packages/psi-verifier-py`) and computes the same schema digest.
package/dist/index.cjs ADDED
@@ -0,0 +1,252 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.ts
20
+ var index_exports = {};
21
+ __export(index_exports, {
22
+ PSI_RULE_CITATIONS: () => PSI_RULE_CITATIONS,
23
+ PSI_SCHEMA_ID: () => PSI_SCHEMA_ID,
24
+ PSI_SCHEMA_RULES: () => PSI_SCHEMA_RULES,
25
+ PSI_SCHEMA_URL: () => PSI_SCHEMA_URL,
26
+ PSI_SEAL_URL: () => PSI_SEAL_URL,
27
+ PSI_VERIFIER_VERSION: () => PSI_VERIFIER_VERSION,
28
+ PsiSealInvalidError: () => PsiSealInvalidError,
29
+ assertSeal: () => assertSeal,
30
+ formatAcceptance: () => formatAcceptance,
31
+ formatRejection: () => formatRejection,
32
+ isConformant: () => isConformant,
33
+ jcs: () => jcs,
34
+ merkleRoot: () => merkleRoot,
35
+ psiLeaf: () => psiLeaf,
36
+ psiSchemaDigest: () => psiSchemaDigest,
37
+ psiSchemaDigestFromDocument: () => psiSchemaDigestFromDocument,
38
+ psiSealHash: () => psiSealHash,
39
+ rejectLine: () => rejectLine,
40
+ verify: () => verify,
41
+ verifySeal: () => verifySeal
42
+ });
43
+ module.exports = __toCommonJS(index_exports);
44
+ var PSI_SCHEMA_ID = "PSI-SEAL/1.0.0";
45
+ var PSI_SCHEMA_RULES = [
46
+ "R1 Envelope serialisation: RFC 8785 JSON Canonicalization Scheme (JCS), UTF-8, no BOM.",
47
+ "R2 Field set is closed. Unknown top-level fields render a seal non-conformant.",
48
+ "R3 Field order in the emitted receipt: schema, schema_digest, sealed_at, subject, hash, merkle, signature, licence.",
49
+ "R4 Digests: lowercase hexadecimal, exactly 64 characters, no 'sha256:' prefix inside the envelope.",
50
+ "R5 Hash algorithm: SHA-256 over the raw octet stream of the subject; no transport encoding, no trailing padding.",
51
+ "R6 sealed_at: RFC 3339 UTC with exactly three fractional digits and a literal 'Z' (e.g. 2026-08-17T09:00:00.000Z).",
52
+ "R7 subject.size_bytes: non-negative integer, exact octet length. subject.name: NFC-normalised UTF-8 string.",
53
+ "R8 Merkle assembly: binary tree over leaf digests in submission order; each parent = SHA-256(left_bytes || right_bytes) over 32-byte raw digests; an odd node is promoted, never duplicated.",
54
+ "R9 merkle.leaf = SHA-256 of the ASCII string 'PSI1:' || hash. Domain separation is mandatory.",
55
+ "R10 seal_hash = SHA-256(JCS(envelope minus the signature and licence members)).",
56
+ "R11 Signature suite: Ed25519 over the ASCII seal_hash; optional hybrid post-quantum LMS-W4-SHA256 (NIST SP 800-208).",
57
+ "R12 Every seal MUST carry schema and schema_digest. Only schema-conformant seals are considered PSI-compliant."
58
+ ];
59
+ function jcs(value) {
60
+ if (value === null || typeof value === "number" || typeof value === "boolean") return JSON.stringify(value);
61
+ if (typeof value === "string") return JSON.stringify(value);
62
+ if (Array.isArray(value)) return `[${value.map(jcs).join(",")}]`;
63
+ if (typeof value === "object") {
64
+ const obj = value;
65
+ const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort(
66
+ (a, b) => a < b ? -1 : a > b ? 1 : 0
67
+ );
68
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${jcs(obj[k])}`).join(",")}}`;
69
+ }
70
+ throw new Error("JCS: unsupported value");
71
+ }
72
+ async function sha256Hex(input) {
73
+ const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
74
+ const buf = await crypto.subtle.digest("SHA-256", bytes);
75
+ return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
76
+ }
77
+ var cachedDigest = null;
78
+ async function psiSchemaDigest() {
79
+ if (cachedDigest) return cachedDigest;
80
+ cachedDigest = await sha256Hex(jcs({ id: PSI_SCHEMA_ID, rules: PSI_SCHEMA_RULES }));
81
+ return cachedDigest;
82
+ }
83
+ async function psiSchemaDigestFromDocument(doc) {
84
+ const d = doc;
85
+ if (typeof (d == null ? void 0 : d.schema) !== "string" || !Array.isArray(d == null ? void 0 : d.rules)) {
86
+ throw new Error("PSI schema document must carry a string `schema` and an array of `rules`");
87
+ }
88
+ return sha256Hex(jcs({ id: d.schema, rules: d.rules }));
89
+ }
90
+ function psiLeaf(hash) {
91
+ return sha256Hex(`PSI1:${hash.replace(/^sha256:/i, "").toLowerCase()}`);
92
+ }
93
+ async function psiSealHash(envelope) {
94
+ const { signature: _s, licence: _l, ...core } = envelope;
95
+ return sha256Hex(jcs(core));
96
+ }
97
+ var HEX64 = /^[0-9a-f]{64}$/;
98
+ var RFC3339_MS = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
99
+ var ALLOWED = ["schema", "schema_digest", "sealed_at", "subject", "hash", "merkle", "signature", "licence"];
100
+ var PSI_VERIFIER_VERSION = "1.2.0";
101
+ var PSI_RULE_CITATIONS = {
102
+ not_object: "1.1",
103
+ closed_field_set: "2.1",
104
+ schema_identifier: "4.1",
105
+ schema_digest: "4.2",
106
+ hash_encoding: "5.1",
107
+ sealed_at: "6.1",
108
+ size_bytes: "7.1",
109
+ leaf_missing: "8.1",
110
+ leaf_mismatch: "9.1",
111
+ seal_hash: "10.1"
112
+ };
113
+ function rejectLine(detail, rule) {
114
+ return `PSI-SEAL v${PSI_VERIFIER_VERSION} REJECT: ${detail} \u2014 rule ${rule}`;
115
+ }
116
+ async function verifySeal(input) {
117
+ var _a, _b;
118
+ const failures = [];
119
+ const env = input;
120
+ if (!env || typeof env !== "object" || Array.isArray(env)) {
121
+ return {
122
+ conformant: false,
123
+ schema_id: null,
124
+ schema_digest_match: false,
125
+ seal_hash_match: false,
126
+ failures: [rejectLine("input is not a JSON object", PSI_RULE_CITATIONS.not_object)]
127
+ };
128
+ }
129
+ const expected = await psiSchemaDigest();
130
+ if (env.schema !== PSI_SCHEMA_ID)
131
+ failures.push(rejectLine(`schema identifier mismatch, expected ${PSI_SCHEMA_ID}`, PSI_RULE_CITATIONS.schema_identifier));
132
+ const digestMatch = env.schema_digest === expected;
133
+ if (!digestMatch) failures.push(rejectLine("schema_digest mismatch", PSI_RULE_CITATIONS.schema_digest));
134
+ const extra = Object.keys(env).filter((k) => !ALLOWED.includes(k));
135
+ if (extra.length)
136
+ failures.push(rejectLine(`unknown top-level fields: ${extra.join(", ")}`, PSI_RULE_CITATIONS.closed_field_set));
137
+ if (!RFC3339_MS.test(env.sealed_at ?? ""))
138
+ failures.push(rejectLine("sealed_at is not RFC 3339 UTC with three fractional digits", PSI_RULE_CITATIONS.sealed_at));
139
+ if (!HEX64.test(env.hash ?? ""))
140
+ failures.push(rejectLine("hash is not 64 lowercase hexadecimal characters", PSI_RULE_CITATIONS.hash_encoding));
141
+ if (!env.subject || !Number.isInteger(env.subject.size_bytes) || env.subject.size_bytes < 0)
142
+ failures.push(rejectLine("subject.size_bytes is not a non-negative integer", PSI_RULE_CITATIONS.size_bytes));
143
+ let sealHashMatch = false;
144
+ if (HEX64.test(env.hash ?? "") && ((_a = env.merkle) == null ? void 0 : _a.leaf)) {
145
+ if (await psiLeaf(env.hash) !== env.merkle.leaf)
146
+ failures.push(rejectLine("merkle.leaf mismatch", PSI_RULE_CITATIONS.leaf_mismatch));
147
+ if ((_b = env.signature) == null ? void 0 : _b.seal_hash) {
148
+ sealHashMatch = await psiSealHash(env) === env.signature.seal_hash;
149
+ if (!sealHashMatch) failures.push(rejectLine("seal_hash mismatch", PSI_RULE_CITATIONS.seal_hash));
150
+ }
151
+ } else {
152
+ failures.push(rejectLine("merkle.leaf missing", PSI_RULE_CITATIONS.leaf_missing));
153
+ }
154
+ return {
155
+ conformant: failures.length === 0,
156
+ schema_id: typeof env.schema === "string" ? env.schema : null,
157
+ schema_digest_match: digestMatch,
158
+ seal_hash_match: sealHashMatch,
159
+ failures
160
+ };
161
+ }
162
+ var PSI_SEAL_URL = "https://ai-governance-standard.com/seal";
163
+ var PSI_SCHEMA_URL = "https://ai-governance-standard.com/.well-known/psi-schema.json";
164
+ var PsiSealInvalidError = class extends Error {
165
+ constructor(result, canonicalDigest) {
166
+ super(formatRejection(result, canonicalDigest));
167
+ this.result = result;
168
+ this.canonicalDigest = canonicalDigest;
169
+ }
170
+ result;
171
+ canonicalDigest;
172
+ name = "PsiSealInvalidError";
173
+ code = "PSI_SEAL_INVALID";
174
+ };
175
+ function formatRejection(result, canonicalDigest) {
176
+ return [
177
+ rejectLine(`seal is not conformant to ${PSI_SCHEMA_ID}`, PSI_RULE_CITATIONS.not_object),
178
+ `canonical schema digest: ${canonicalDigest}`,
179
+ `received schema: ${result.schema_id ?? "(none)"} (digest match: ${result.schema_digest_match ? "true" : "false"})`,
180
+ "findings:",
181
+ ...result.failures.map((f) => ` ${f}`),
182
+ `generate a conformant seal: ${PSI_SEAL_URL}`,
183
+ `canonical schema: ${PSI_SCHEMA_URL}`,
184
+ "legacy escape hatch (report-only): enforce=false",
185
+ "verified, not asserted; trust the math, not the maker."
186
+ ].join("\n");
187
+ }
188
+ function formatAcceptance() {
189
+ return `PSI-SEAL v${PSI_VERIFIER_VERSION} ACCEPT: conformant seal`;
190
+ }
191
+ async function verify(input, options = {}) {
192
+ const enforce = options.enforce !== false;
193
+ const result = await verifySeal(input);
194
+ if (!result.conformant && enforce) {
195
+ throw new PsiSealInvalidError(result, await psiSchemaDigest());
196
+ }
197
+ return result;
198
+ }
199
+ async function isConformant(input) {
200
+ return (await verifySeal(input)).conformant;
201
+ }
202
+ async function assertSeal(input) {
203
+ return verify(input, { enforce: true });
204
+ }
205
+ async function merkleRoot(leaves) {
206
+ if (!leaves.length) throw new Error("R8: at least one leaf required");
207
+ const toBytes = (h) => {
208
+ const c = h.toLowerCase();
209
+ const out = new Uint8Array(new ArrayBuffer(32));
210
+ for (let i = 0; i < 32; i++) out[i] = parseInt(c.slice(i * 2, i * 2 + 2), 16);
211
+ return out;
212
+ };
213
+ let level = leaves.map(toBytes);
214
+ while (level.length > 1) {
215
+ const next = [];
216
+ for (let i = 0; i < level.length; i += 2) {
217
+ if (i + 1 === level.length) {
218
+ next.push(level[i]);
219
+ continue;
220
+ }
221
+ const merged = new Uint8Array(new ArrayBuffer(64));
222
+ merged.set(level[i], 0);
223
+ merged.set(level[i + 1], 32);
224
+ next.push(toBytes(await sha256Hex(merged)));
225
+ }
226
+ level = next;
227
+ }
228
+ return Array.from(level[0]).map((b) => b.toString(16).padStart(2, "0")).join("");
229
+ }
230
+ // Annotate the CommonJS export names for ESM import in node:
231
+ 0 && (module.exports = {
232
+ PSI_RULE_CITATIONS,
233
+ PSI_SCHEMA_ID,
234
+ PSI_SCHEMA_RULES,
235
+ PSI_SCHEMA_URL,
236
+ PSI_SEAL_URL,
237
+ PSI_VERIFIER_VERSION,
238
+ PsiSealInvalidError,
239
+ assertSeal,
240
+ formatAcceptance,
241
+ formatRejection,
242
+ isConformant,
243
+ jcs,
244
+ merkleRoot,
245
+ psiLeaf,
246
+ psiSchemaDigest,
247
+ psiSchemaDigestFromDocument,
248
+ psiSealHash,
249
+ rejectLine,
250
+ verify,
251
+ verifySeal
252
+ });
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @apex/psi-verifier — MIT licensed. Free forever.
3
+ *
4
+ * Verifies that a PSI seal conforms, byte for byte, to the copyrighted
5
+ * PSI-SEAL/1 canonical schema. Verification is open to everyone; producing
6
+ * conformant seals requires the licensed APEX PSI Sealing Engine.
7
+ *
8
+ * Zero dependencies. Runs in browsers, Node 18+, Deno, Bun and workers.
9
+ */
10
+ declare const PSI_SCHEMA_ID = "PSI-SEAL/1.0.0";
11
+ declare const PSI_SCHEMA_RULES: readonly ["R1 Envelope serialisation: RFC 8785 JSON Canonicalization Scheme (JCS), UTF-8, no BOM.", "R2 Field set is closed. Unknown top-level fields render a seal non-conformant.", "R3 Field order in the emitted receipt: schema, schema_digest, sealed_at, subject, hash, merkle, signature, licence.", "R4 Digests: lowercase hexadecimal, exactly 64 characters, no 'sha256:' prefix inside the envelope.", "R5 Hash algorithm: SHA-256 over the raw octet stream of the subject; no transport encoding, no trailing padding.", "R6 sealed_at: RFC 3339 UTC with exactly three fractional digits and a literal 'Z' (e.g. 2026-08-17T09:00:00.000Z).", "R7 subject.size_bytes: non-negative integer, exact octet length. subject.name: NFC-normalised UTF-8 string.", "R8 Merkle assembly: binary tree over leaf digests in submission order; each parent = SHA-256(left_bytes || right_bytes) over 32-byte raw digests; an odd node is promoted, never duplicated.", "R9 merkle.leaf = SHA-256 of the ASCII string 'PSI1:' || hash. Domain separation is mandatory.", "R10 seal_hash = SHA-256(JCS(envelope minus the signature and licence members)).", "R11 Signature suite: Ed25519 over the ASCII seal_hash; optional hybrid post-quantum LMS-W4-SHA256 (NIST SP 800-208).", "R12 Every seal MUST carry schema and schema_digest. Only schema-conformant seals are considered PSI-compliant."];
12
+ interface PsiSealEnvelope {
13
+ schema: string;
14
+ schema_digest: string;
15
+ sealed_at: string;
16
+ subject: {
17
+ name: string;
18
+ size_bytes: number;
19
+ media_type?: string;
20
+ };
21
+ hash: string;
22
+ merkle: {
23
+ leaf: string;
24
+ root?: string;
25
+ proof?: string[];
26
+ };
27
+ signature?: {
28
+ alg: string;
29
+ value: string;
30
+ seal_hash: string;
31
+ pq_alg?: string;
32
+ pq_value?: string;
33
+ };
34
+ licence?: {
35
+ engine: string;
36
+ tier: string;
37
+ terms: string;
38
+ accepted_at: string;
39
+ };
40
+ }
41
+ interface ConformanceResult {
42
+ conformant: boolean;
43
+ schema_id: string | null;
44
+ schema_digest_match: boolean;
45
+ seal_hash_match: boolean;
46
+ failures: string[];
47
+ }
48
+ declare function jcs(value: unknown): string;
49
+ declare function psiSchemaDigest(): Promise<string>;
50
+ /**
51
+ * Compute the schema digest from a published schema document (for example the
52
+ * one served at /.well-known/psi-schema.json) using this verifier's own
53
+ * canonicalisation. Anyone can run this against the npm or PyPI package output
54
+ * and compare the 64-hex result: verified, not asserted.
55
+ */
56
+ declare function psiSchemaDigestFromDocument(doc: unknown): Promise<string>;
57
+ declare function psiLeaf(hash: string): Promise<string>;
58
+ declare function psiSealHash(envelope: PsiSealEnvelope): Promise<string>;
59
+ /** Verifier distribution version. Identical in the Python distribution. */
60
+ declare const PSI_VERIFIER_VERSION = "1.2.0";
61
+ /**
62
+ * Human-readable rule citations (v1.2.0). Every rejection line carries one:
63
+ * verified, not asserted — trust the math, not the maker.
64
+ */
65
+ declare const PSI_RULE_CITATIONS: {
66
+ readonly not_object: "1.1";
67
+ readonly closed_field_set: "2.1";
68
+ readonly schema_identifier: "4.1";
69
+ readonly schema_digest: "4.2";
70
+ readonly hash_encoding: "5.1";
71
+ readonly sealed_at: "6.1";
72
+ readonly size_bytes: "7.1";
73
+ readonly leaf_missing: "8.1";
74
+ readonly leaf_mismatch: "9.1";
75
+ readonly seal_hash: "10.1";
76
+ };
77
+ /** Canonical single-line rejection. Byte-identical in TypeScript and Python. */
78
+ declare function rejectLine(detail: string, rule: string): string;
79
+ declare function verifySeal(input: unknown): Promise<ConformanceResult>;
80
+ /** Where a valid seal is produced. Printed in every rejection. */
81
+ declare const PSI_SEAL_URL = "https://ai-governance-standard.com/seal";
82
+ /** Machine-readable schema + licence discovery document. */
83
+ declare const PSI_SCHEMA_URL = "https://ai-governance-standard.com/.well-known/psi-schema.json";
84
+ /** Thrown by `verify()` when `enforce` is true (the default) and the seal fails. */
85
+ declare class PsiSealInvalidError extends Error {
86
+ readonly result: ConformanceResult;
87
+ readonly canonicalDigest: string;
88
+ readonly name = "PsiSealInvalidError";
89
+ readonly code = "PSI_SEAL_INVALID";
90
+ constructor(result: ConformanceResult, canonicalDigest: string);
91
+ }
92
+ /**
93
+ * Canonical rejection text. Stable, greppable, actionable, and byte-identical
94
+ * to the Python distribution for the same input.
95
+ */
96
+ declare function formatRejection(result: ConformanceResult, canonicalDigest: string): string;
97
+ /** Canonical acceptance line. Byte-identical in the Python distribution. */
98
+ declare function formatAcceptance(): string;
99
+ interface VerifyOptions {
100
+ /**
101
+ * Reject non-conformant seals by throwing `PsiSealInvalidError`.
102
+ * DEFAULT: true (changed in v1.1.0 — previously report-only).
103
+ * Set false only to inspect legacy data without failing the pipeline.
104
+ */
105
+ enforce?: boolean;
106
+ }
107
+ /**
108
+ * Primary entry point. Enforced by default.
109
+ *
110
+ * @throws PsiSealInvalidError when the seal is not PSI-SEAL/1 conformant
111
+ * and `enforce` is left at its default (true).
112
+ */
113
+ declare function verify(input: unknown, options?: VerifyOptions): Promise<ConformanceResult>;
114
+ /** Boolean convenience form. Never throws. */
115
+ declare function isConformant(input: unknown): Promise<boolean>;
116
+ /** Throwing assertion, identical to `verify(input)`. */
117
+ declare function assertSeal(input: unknown): Promise<ConformanceResult>;
118
+ /** Canonical Merkle root (R8) — promoted odd node, raw 32-byte concatenation. */
119
+ declare function merkleRoot(leaves: string[]): Promise<string>;
120
+
121
+ export { type ConformanceResult, PSI_RULE_CITATIONS, PSI_SCHEMA_ID, PSI_SCHEMA_RULES, PSI_SCHEMA_URL, PSI_SEAL_URL, PSI_VERIFIER_VERSION, type PsiSealEnvelope, PsiSealInvalidError, type VerifyOptions, assertSeal, formatAcceptance, formatRejection, isConformant, jcs, merkleRoot, psiLeaf, psiSchemaDigest, psiSchemaDigestFromDocument, psiSealHash, rejectLine, verify, verifySeal };
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @apex/psi-verifier — MIT licensed. Free forever.
3
+ *
4
+ * Verifies that a PSI seal conforms, byte for byte, to the copyrighted
5
+ * PSI-SEAL/1 canonical schema. Verification is open to everyone; producing
6
+ * conformant seals requires the licensed APEX PSI Sealing Engine.
7
+ *
8
+ * Zero dependencies. Runs in browsers, Node 18+, Deno, Bun and workers.
9
+ */
10
+ declare const PSI_SCHEMA_ID = "PSI-SEAL/1.0.0";
11
+ declare const PSI_SCHEMA_RULES: readonly ["R1 Envelope serialisation: RFC 8785 JSON Canonicalization Scheme (JCS), UTF-8, no BOM.", "R2 Field set is closed. Unknown top-level fields render a seal non-conformant.", "R3 Field order in the emitted receipt: schema, schema_digest, sealed_at, subject, hash, merkle, signature, licence.", "R4 Digests: lowercase hexadecimal, exactly 64 characters, no 'sha256:' prefix inside the envelope.", "R5 Hash algorithm: SHA-256 over the raw octet stream of the subject; no transport encoding, no trailing padding.", "R6 sealed_at: RFC 3339 UTC with exactly three fractional digits and a literal 'Z' (e.g. 2026-08-17T09:00:00.000Z).", "R7 subject.size_bytes: non-negative integer, exact octet length. subject.name: NFC-normalised UTF-8 string.", "R8 Merkle assembly: binary tree over leaf digests in submission order; each parent = SHA-256(left_bytes || right_bytes) over 32-byte raw digests; an odd node is promoted, never duplicated.", "R9 merkle.leaf = SHA-256 of the ASCII string 'PSI1:' || hash. Domain separation is mandatory.", "R10 seal_hash = SHA-256(JCS(envelope minus the signature and licence members)).", "R11 Signature suite: Ed25519 over the ASCII seal_hash; optional hybrid post-quantum LMS-W4-SHA256 (NIST SP 800-208).", "R12 Every seal MUST carry schema and schema_digest. Only schema-conformant seals are considered PSI-compliant."];
12
+ interface PsiSealEnvelope {
13
+ schema: string;
14
+ schema_digest: string;
15
+ sealed_at: string;
16
+ subject: {
17
+ name: string;
18
+ size_bytes: number;
19
+ media_type?: string;
20
+ };
21
+ hash: string;
22
+ merkle: {
23
+ leaf: string;
24
+ root?: string;
25
+ proof?: string[];
26
+ };
27
+ signature?: {
28
+ alg: string;
29
+ value: string;
30
+ seal_hash: string;
31
+ pq_alg?: string;
32
+ pq_value?: string;
33
+ };
34
+ licence?: {
35
+ engine: string;
36
+ tier: string;
37
+ terms: string;
38
+ accepted_at: string;
39
+ };
40
+ }
41
+ interface ConformanceResult {
42
+ conformant: boolean;
43
+ schema_id: string | null;
44
+ schema_digest_match: boolean;
45
+ seal_hash_match: boolean;
46
+ failures: string[];
47
+ }
48
+ declare function jcs(value: unknown): string;
49
+ declare function psiSchemaDigest(): Promise<string>;
50
+ /**
51
+ * Compute the schema digest from a published schema document (for example the
52
+ * one served at /.well-known/psi-schema.json) using this verifier's own
53
+ * canonicalisation. Anyone can run this against the npm or PyPI package output
54
+ * and compare the 64-hex result: verified, not asserted.
55
+ */
56
+ declare function psiSchemaDigestFromDocument(doc: unknown): Promise<string>;
57
+ declare function psiLeaf(hash: string): Promise<string>;
58
+ declare function psiSealHash(envelope: PsiSealEnvelope): Promise<string>;
59
+ /** Verifier distribution version. Identical in the Python distribution. */
60
+ declare const PSI_VERIFIER_VERSION = "1.2.0";
61
+ /**
62
+ * Human-readable rule citations (v1.2.0). Every rejection line carries one:
63
+ * verified, not asserted — trust the math, not the maker.
64
+ */
65
+ declare const PSI_RULE_CITATIONS: {
66
+ readonly not_object: "1.1";
67
+ readonly closed_field_set: "2.1";
68
+ readonly schema_identifier: "4.1";
69
+ readonly schema_digest: "4.2";
70
+ readonly hash_encoding: "5.1";
71
+ readonly sealed_at: "6.1";
72
+ readonly size_bytes: "7.1";
73
+ readonly leaf_missing: "8.1";
74
+ readonly leaf_mismatch: "9.1";
75
+ readonly seal_hash: "10.1";
76
+ };
77
+ /** Canonical single-line rejection. Byte-identical in TypeScript and Python. */
78
+ declare function rejectLine(detail: string, rule: string): string;
79
+ declare function verifySeal(input: unknown): Promise<ConformanceResult>;
80
+ /** Where a valid seal is produced. Printed in every rejection. */
81
+ declare const PSI_SEAL_URL = "https://ai-governance-standard.com/seal";
82
+ /** Machine-readable schema + licence discovery document. */
83
+ declare const PSI_SCHEMA_URL = "https://ai-governance-standard.com/.well-known/psi-schema.json";
84
+ /** Thrown by `verify()` when `enforce` is true (the default) and the seal fails. */
85
+ declare class PsiSealInvalidError extends Error {
86
+ readonly result: ConformanceResult;
87
+ readonly canonicalDigest: string;
88
+ readonly name = "PsiSealInvalidError";
89
+ readonly code = "PSI_SEAL_INVALID";
90
+ constructor(result: ConformanceResult, canonicalDigest: string);
91
+ }
92
+ /**
93
+ * Canonical rejection text. Stable, greppable, actionable, and byte-identical
94
+ * to the Python distribution for the same input.
95
+ */
96
+ declare function formatRejection(result: ConformanceResult, canonicalDigest: string): string;
97
+ /** Canonical acceptance line. Byte-identical in the Python distribution. */
98
+ declare function formatAcceptance(): string;
99
+ interface VerifyOptions {
100
+ /**
101
+ * Reject non-conformant seals by throwing `PsiSealInvalidError`.
102
+ * DEFAULT: true (changed in v1.1.0 — previously report-only).
103
+ * Set false only to inspect legacy data without failing the pipeline.
104
+ */
105
+ enforce?: boolean;
106
+ }
107
+ /**
108
+ * Primary entry point. Enforced by default.
109
+ *
110
+ * @throws PsiSealInvalidError when the seal is not PSI-SEAL/1 conformant
111
+ * and `enforce` is left at its default (true).
112
+ */
113
+ declare function verify(input: unknown, options?: VerifyOptions): Promise<ConformanceResult>;
114
+ /** Boolean convenience form. Never throws. */
115
+ declare function isConformant(input: unknown): Promise<boolean>;
116
+ /** Throwing assertion, identical to `verify(input)`. */
117
+ declare function assertSeal(input: unknown): Promise<ConformanceResult>;
118
+ /** Canonical Merkle root (R8) — promoted odd node, raw 32-byte concatenation. */
119
+ declare function merkleRoot(leaves: string[]): Promise<string>;
120
+
121
+ export { type ConformanceResult, PSI_RULE_CITATIONS, PSI_SCHEMA_ID, PSI_SCHEMA_RULES, PSI_SCHEMA_URL, PSI_SEAL_URL, PSI_VERIFIER_VERSION, type PsiSealEnvelope, PsiSealInvalidError, type VerifyOptions, assertSeal, formatAcceptance, formatRejection, isConformant, jcs, merkleRoot, psiLeaf, psiSchemaDigest, psiSchemaDigestFromDocument, psiSealHash, rejectLine, verify, verifySeal };
package/dist/index.js ADDED
@@ -0,0 +1,209 @@
1
+ // src/index.ts
2
+ var PSI_SCHEMA_ID = "PSI-SEAL/1.0.0";
3
+ var PSI_SCHEMA_RULES = [
4
+ "R1 Envelope serialisation: RFC 8785 JSON Canonicalization Scheme (JCS), UTF-8, no BOM.",
5
+ "R2 Field set is closed. Unknown top-level fields render a seal non-conformant.",
6
+ "R3 Field order in the emitted receipt: schema, schema_digest, sealed_at, subject, hash, merkle, signature, licence.",
7
+ "R4 Digests: lowercase hexadecimal, exactly 64 characters, no 'sha256:' prefix inside the envelope.",
8
+ "R5 Hash algorithm: SHA-256 over the raw octet stream of the subject; no transport encoding, no trailing padding.",
9
+ "R6 sealed_at: RFC 3339 UTC with exactly three fractional digits and a literal 'Z' (e.g. 2026-08-17T09:00:00.000Z).",
10
+ "R7 subject.size_bytes: non-negative integer, exact octet length. subject.name: NFC-normalised UTF-8 string.",
11
+ "R8 Merkle assembly: binary tree over leaf digests in submission order; each parent = SHA-256(left_bytes || right_bytes) over 32-byte raw digests; an odd node is promoted, never duplicated.",
12
+ "R9 merkle.leaf = SHA-256 of the ASCII string 'PSI1:' || hash. Domain separation is mandatory.",
13
+ "R10 seal_hash = SHA-256(JCS(envelope minus the signature and licence members)).",
14
+ "R11 Signature suite: Ed25519 over the ASCII seal_hash; optional hybrid post-quantum LMS-W4-SHA256 (NIST SP 800-208).",
15
+ "R12 Every seal MUST carry schema and schema_digest. Only schema-conformant seals are considered PSI-compliant."
16
+ ];
17
+ function jcs(value) {
18
+ if (value === null || typeof value === "number" || typeof value === "boolean") return JSON.stringify(value);
19
+ if (typeof value === "string") return JSON.stringify(value);
20
+ if (Array.isArray(value)) return `[${value.map(jcs).join(",")}]`;
21
+ if (typeof value === "object") {
22
+ const obj = value;
23
+ const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort(
24
+ (a, b) => a < b ? -1 : a > b ? 1 : 0
25
+ );
26
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${jcs(obj[k])}`).join(",")}}`;
27
+ }
28
+ throw new Error("JCS: unsupported value");
29
+ }
30
+ async function sha256Hex(input) {
31
+ const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
32
+ const buf = await crypto.subtle.digest("SHA-256", bytes);
33
+ return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
34
+ }
35
+ var cachedDigest = null;
36
+ async function psiSchemaDigest() {
37
+ if (cachedDigest) return cachedDigest;
38
+ cachedDigest = await sha256Hex(jcs({ id: PSI_SCHEMA_ID, rules: PSI_SCHEMA_RULES }));
39
+ return cachedDigest;
40
+ }
41
+ async function psiSchemaDigestFromDocument(doc) {
42
+ const d = doc;
43
+ if (typeof (d == null ? void 0 : d.schema) !== "string" || !Array.isArray(d == null ? void 0 : d.rules)) {
44
+ throw new Error("PSI schema document must carry a string `schema` and an array of `rules`");
45
+ }
46
+ return sha256Hex(jcs({ id: d.schema, rules: d.rules }));
47
+ }
48
+ function psiLeaf(hash) {
49
+ return sha256Hex(`PSI1:${hash.replace(/^sha256:/i, "").toLowerCase()}`);
50
+ }
51
+ async function psiSealHash(envelope) {
52
+ const { signature: _s, licence: _l, ...core } = envelope;
53
+ return sha256Hex(jcs(core));
54
+ }
55
+ var HEX64 = /^[0-9a-f]{64}$/;
56
+ var RFC3339_MS = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
57
+ var ALLOWED = ["schema", "schema_digest", "sealed_at", "subject", "hash", "merkle", "signature", "licence"];
58
+ var PSI_VERIFIER_VERSION = "1.2.0";
59
+ var PSI_RULE_CITATIONS = {
60
+ not_object: "1.1",
61
+ closed_field_set: "2.1",
62
+ schema_identifier: "4.1",
63
+ schema_digest: "4.2",
64
+ hash_encoding: "5.1",
65
+ sealed_at: "6.1",
66
+ size_bytes: "7.1",
67
+ leaf_missing: "8.1",
68
+ leaf_mismatch: "9.1",
69
+ seal_hash: "10.1"
70
+ };
71
+ function rejectLine(detail, rule) {
72
+ return `PSI-SEAL v${PSI_VERIFIER_VERSION} REJECT: ${detail} \u2014 rule ${rule}`;
73
+ }
74
+ async function verifySeal(input) {
75
+ var _a, _b;
76
+ const failures = [];
77
+ const env = input;
78
+ if (!env || typeof env !== "object" || Array.isArray(env)) {
79
+ return {
80
+ conformant: false,
81
+ schema_id: null,
82
+ schema_digest_match: false,
83
+ seal_hash_match: false,
84
+ failures: [rejectLine("input is not a JSON object", PSI_RULE_CITATIONS.not_object)]
85
+ };
86
+ }
87
+ const expected = await psiSchemaDigest();
88
+ if (env.schema !== PSI_SCHEMA_ID)
89
+ failures.push(rejectLine(`schema identifier mismatch, expected ${PSI_SCHEMA_ID}`, PSI_RULE_CITATIONS.schema_identifier));
90
+ const digestMatch = env.schema_digest === expected;
91
+ if (!digestMatch) failures.push(rejectLine("schema_digest mismatch", PSI_RULE_CITATIONS.schema_digest));
92
+ const extra = Object.keys(env).filter((k) => !ALLOWED.includes(k));
93
+ if (extra.length)
94
+ failures.push(rejectLine(`unknown top-level fields: ${extra.join(", ")}`, PSI_RULE_CITATIONS.closed_field_set));
95
+ if (!RFC3339_MS.test(env.sealed_at ?? ""))
96
+ failures.push(rejectLine("sealed_at is not RFC 3339 UTC with three fractional digits", PSI_RULE_CITATIONS.sealed_at));
97
+ if (!HEX64.test(env.hash ?? ""))
98
+ failures.push(rejectLine("hash is not 64 lowercase hexadecimal characters", PSI_RULE_CITATIONS.hash_encoding));
99
+ if (!env.subject || !Number.isInteger(env.subject.size_bytes) || env.subject.size_bytes < 0)
100
+ failures.push(rejectLine("subject.size_bytes is not a non-negative integer", PSI_RULE_CITATIONS.size_bytes));
101
+ let sealHashMatch = false;
102
+ if (HEX64.test(env.hash ?? "") && ((_a = env.merkle) == null ? void 0 : _a.leaf)) {
103
+ if (await psiLeaf(env.hash) !== env.merkle.leaf)
104
+ failures.push(rejectLine("merkle.leaf mismatch", PSI_RULE_CITATIONS.leaf_mismatch));
105
+ if ((_b = env.signature) == null ? void 0 : _b.seal_hash) {
106
+ sealHashMatch = await psiSealHash(env) === env.signature.seal_hash;
107
+ if (!sealHashMatch) failures.push(rejectLine("seal_hash mismatch", PSI_RULE_CITATIONS.seal_hash));
108
+ }
109
+ } else {
110
+ failures.push(rejectLine("merkle.leaf missing", PSI_RULE_CITATIONS.leaf_missing));
111
+ }
112
+ return {
113
+ conformant: failures.length === 0,
114
+ schema_id: typeof env.schema === "string" ? env.schema : null,
115
+ schema_digest_match: digestMatch,
116
+ seal_hash_match: sealHashMatch,
117
+ failures
118
+ };
119
+ }
120
+ var PSI_SEAL_URL = "https://ai-governance-standard.com/seal";
121
+ var PSI_SCHEMA_URL = "https://ai-governance-standard.com/.well-known/psi-schema.json";
122
+ var PsiSealInvalidError = class extends Error {
123
+ constructor(result, canonicalDigest) {
124
+ super(formatRejection(result, canonicalDigest));
125
+ this.result = result;
126
+ this.canonicalDigest = canonicalDigest;
127
+ }
128
+ result;
129
+ canonicalDigest;
130
+ name = "PsiSealInvalidError";
131
+ code = "PSI_SEAL_INVALID";
132
+ };
133
+ function formatRejection(result, canonicalDigest) {
134
+ return [
135
+ rejectLine(`seal is not conformant to ${PSI_SCHEMA_ID}`, PSI_RULE_CITATIONS.not_object),
136
+ `canonical schema digest: ${canonicalDigest}`,
137
+ `received schema: ${result.schema_id ?? "(none)"} (digest match: ${result.schema_digest_match ? "true" : "false"})`,
138
+ "findings:",
139
+ ...result.failures.map((f) => ` ${f}`),
140
+ `generate a conformant seal: ${PSI_SEAL_URL}`,
141
+ `canonical schema: ${PSI_SCHEMA_URL}`,
142
+ "legacy escape hatch (report-only): enforce=false",
143
+ "verified, not asserted; trust the math, not the maker."
144
+ ].join("\n");
145
+ }
146
+ function formatAcceptance() {
147
+ return `PSI-SEAL v${PSI_VERIFIER_VERSION} ACCEPT: conformant seal`;
148
+ }
149
+ async function verify(input, options = {}) {
150
+ const enforce = options.enforce !== false;
151
+ const result = await verifySeal(input);
152
+ if (!result.conformant && enforce) {
153
+ throw new PsiSealInvalidError(result, await psiSchemaDigest());
154
+ }
155
+ return result;
156
+ }
157
+ async function isConformant(input) {
158
+ return (await verifySeal(input)).conformant;
159
+ }
160
+ async function assertSeal(input) {
161
+ return verify(input, { enforce: true });
162
+ }
163
+ async function merkleRoot(leaves) {
164
+ if (!leaves.length) throw new Error("R8: at least one leaf required");
165
+ const toBytes = (h) => {
166
+ const c = h.toLowerCase();
167
+ const out = new Uint8Array(new ArrayBuffer(32));
168
+ for (let i = 0; i < 32; i++) out[i] = parseInt(c.slice(i * 2, i * 2 + 2), 16);
169
+ return out;
170
+ };
171
+ let level = leaves.map(toBytes);
172
+ while (level.length > 1) {
173
+ const next = [];
174
+ for (let i = 0; i < level.length; i += 2) {
175
+ if (i + 1 === level.length) {
176
+ next.push(level[i]);
177
+ continue;
178
+ }
179
+ const merged = new Uint8Array(new ArrayBuffer(64));
180
+ merged.set(level[i], 0);
181
+ merged.set(level[i + 1], 32);
182
+ next.push(toBytes(await sha256Hex(merged)));
183
+ }
184
+ level = next;
185
+ }
186
+ return Array.from(level[0]).map((b) => b.toString(16).padStart(2, "0")).join("");
187
+ }
188
+ export {
189
+ PSI_RULE_CITATIONS,
190
+ PSI_SCHEMA_ID,
191
+ PSI_SCHEMA_RULES,
192
+ PSI_SCHEMA_URL,
193
+ PSI_SEAL_URL,
194
+ PSI_VERIFIER_VERSION,
195
+ PsiSealInvalidError,
196
+ assertSeal,
197
+ formatAcceptance,
198
+ formatRejection,
199
+ isConformant,
200
+ jcs,
201
+ merkleRoot,
202
+ psiLeaf,
203
+ psiSchemaDigest,
204
+ psiSchemaDigestFromDocument,
205
+ psiSealHash,
206
+ rejectLine,
207
+ verify,
208
+ verifySeal
209
+ };
package/package.json CHANGED
@@ -1,6 +1,61 @@
1
1
  {
2
2
  "name": "apex-psi-verifier",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.2.0",
4
+ "description": "MIT-licensed verifier for the PSI-SEAL/1 canonical seal schema. Verification is free forever; non-conformant seals are rejected by default.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.cjs",
8
+ "types": "dist/index.d.ts",
9
+ "module": "dist/index.js",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE",
21
+ "CHANGELOG.md"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup src/index.ts --format cjs,esm --dts",
25
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
26
+ "prepublishOnly": "npm run build"
27
+ },
28
+ "keywords": [
29
+ "psi",
30
+ "apex",
31
+ "apex-psi",
32
+ "verification",
33
+ "verifier",
34
+ "ai-governance",
35
+ "eu-ai-act",
36
+ "sha256",
37
+ "ed25519",
38
+ "merkle-tree",
39
+ "rfc8785",
40
+ "post-quantum",
41
+ "provenance"
42
+ ],
43
+ "author": "APEX Intelligence Empire / ROCKYFILMS888 PTY LTD (ABN 71 672 237 795)",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/kawal393/APEX-PSI.git",
47
+ "directory": "packages/psi-verifier"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/kawal393/APEX-PSI/issues"
51
+ },
52
+ "homepage": "https://ai-governance-standard.com/license",
53
+ "engines": {
54
+ "node": ">=18"
55
+ },
56
+ "devDependencies": {
57
+ "tsup": "^8.0.0",
58
+ "typescript": "^5.0.0"
59
+ },
60
+ "sideEffects": false
6
61
  }