@crovia/seal 0.1.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/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 Crovia Trust
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # @crovia/seal
2
+
3
+ **Immutable continuity receipts for evolving AI systems.**
4
+
5
+ Tiny, offline, cryptographic primitive. Sign any JSON payload, get a verifiable receipt. No network call required.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @crovia/seal
11
+ ```
12
+
13
+ ## Use
14
+
15
+ ```ts
16
+ import { seal, verify, generateKeySync } from "@crovia/seal";
17
+
18
+ const key = generateKeySync();
19
+
20
+ // Sign any JSON payload.
21
+ const receipt = await seal(
22
+ { model: "gpt-4o", output: "Hello, world." },
23
+ { key },
24
+ );
25
+
26
+ // Verify with the original payload (full check).
27
+ const result = await verify(receipt, { model: "gpt-4o", output: "Hello, world." });
28
+ console.log(result.valid); // true
29
+ ```
30
+
31
+ ## What you get
32
+
33
+ - **Cryptographic signature** — Ed25519, byte-identical with the Python reference.
34
+ - **Stable receipt id** — `cr_YYYY_<26 base32>`, collision-resistant.
35
+ - **Continuity** — chain receipts via `prevReceipt` to get an immutable lineage.
36
+ - **Offline by default** — no network. Works in CI, behind firewalls, in isolated envs.
37
+ - **Public continuity graph** — opt-in via `register(receipt)` to publish to the Crovia substrate.
38
+
39
+ ## Continuity chain
40
+
41
+ ```ts
42
+ const r1 = await seal({ version: 1, content: "..." }, { key });
43
+ const r2 = await seal({ version: 2, content: "..." }, { key, prevReceipt: r1 });
44
+ const r3 = await seal({ version: 3, content: "..." }, { key, prevReceipt: r2 });
45
+
46
+ // Verify the whole chain.
47
+ import { verifyChain } from "@crovia/seal";
48
+ const result = await verifyChain([r1, r2, r3]);
49
+ ```
50
+
51
+ ## Optional: publish to the substrate
52
+
53
+ ```ts
54
+ import { register } from "@crovia/seal";
55
+
56
+ const r = await seal(payload, { key });
57
+ const ack = await register(r); // posts to https://croviatrust.com/api/anchor
58
+ console.log(ack.accepted, ack.anchorId);
59
+ ```
60
+
61
+ This is the only call that touches the network. `seal()` and `verify()` never do.
62
+
63
+ ## Receipt format (`crovia.receipt.v1`)
64
+
65
+ ```json
66
+ {
67
+ "v": "crovia.receipt.v1",
68
+ "id": "cr_2026_AB23CD45EF67GH89IJ012KLMN3",
69
+ "issued_at": "2026-05-07T15:43:57.123Z",
70
+ "payload_hash": "sha256:<64 hex>",
71
+ "payload_alg": "sha256",
72
+ "prev": null,
73
+ "seq": 0,
74
+ "signer": "<64 hex Ed25519 pubkey>",
75
+ "sig_alg": "ed25519",
76
+ "canon": "csc-1",
77
+ "domain": "CROVIA-RECEIPT-v1",
78
+ "sig": "<128 hex Ed25519 signature>"
79
+ }
80
+ ```
81
+
82
+ The signature covers `b"CROVIA-RECEIPT-v1\n" || csc1(receipt without sig)`.
83
+
84
+ ## License
85
+
86
+ Apache-2.0.
87
+
88
+ ## Spec & reference impls
89
+
90
+ - Spec: <https://croviatrust.com/seal>
91
+ - Python equivalent: `pip install crovia-seal`
92
+ - Source: <https://github.com/croviatrust/crovia-seal/tree/main/sdk/javascript>
@@ -0,0 +1,10 @@
1
+ function Me(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name==="Uint8Array"}function it(t,...e){if(!Me(t))throw new Error("Uint8Array expected");if(e.length>0&&!e.includes(t.length))throw new Error("Uint8Array expected of length "+e+", got length="+t.length)}function St(t,e=!0){if(t.destroyed)throw new Error("Hash instance has been destroyed");if(e&&t.finished)throw new Error("Hash#digest() has already been called")}function Jt(t,e){it(t);let n=e.outputLen;if(t.length<n)throw new Error("digestInto() expects output buffer of length at least "+n)}function D(...t){for(let e=0;e<t.length;e++)t[e].fill(0)}function ct(t){return new DataView(t.buffer,t.byteOffset,t.byteLength)}function _(t,e){return t<<32-e|t>>>e}function qe(t){if(typeof t!="string")throw new Error("string expected");return new Uint8Array(new TextEncoder().encode(t))}function Bt(t){return typeof t=="string"&&(t=qe(t)),it(t),t}var ot=class{};function Ht(t){let e=r=>t().update(Bt(r)).digest(),n=t();return e.outputLen=n.outputLen,e.blockLen=n.blockLen,e.create=()=>t(),e}function ze(t,e,n,r){if(typeof t.setBigUint64=="function")return t.setBigUint64(e,n,r);let s=BigInt(32),o=BigInt(4294967295),i=Number(n>>s&o),a=Number(n&o),c=r?4:0,u=r?0:4;t.setUint32(e+c,i,r),t.setUint32(e+u,a,r)}function Wt(t,e,n){return t&e^~t&n}function Qt(t,e,n){return t&e^t&n^e&n}var W=class extends ot{constructor(e,n,r,s){super(),this.finished=!1,this.length=0,this.pos=0,this.destroyed=!1,this.blockLen=e,this.outputLen=n,this.padOffset=r,this.isLE=s,this.buffer=new Uint8Array(e),this.view=ct(this.buffer)}update(e){St(this),e=Bt(e),it(e);let{view:n,buffer:r,blockLen:s}=this,o=e.length;for(let i=0;i<o;){let a=Math.min(s-this.pos,o-i);if(a===s){let c=ct(e);for(;s<=o-i;i+=s)this.process(c,i);continue}r.set(e.subarray(i,i+a),this.pos),this.pos+=a,i+=a,this.pos===s&&(this.process(n,0),this.pos=0)}return this.length+=e.length,this.roundClean(),this}digestInto(e){St(this),Jt(e,this),this.finished=!0;let{buffer:n,view:r,blockLen:s,isLE:o}=this,{pos:i}=this;n[i++]=128,D(this.buffer.subarray(i)),this.padOffset>s-i&&(this.process(r,0),i=0);for(let f=i;f<s;f++)n[f]=0;ze(r,s-8,BigInt(this.length*8),o),this.process(r,0);let a=ct(e),c=this.outputLen;if(c%4)throw new Error("_sha2: outputLen should be aligned to 32bit");let u=c/4,l=this.get();if(u>l.length)throw new Error("_sha2: outputLen bigger than state");for(let f=0;f<u;f++)a.setUint32(4*f,l[f],o)}digest(){let{buffer:e,outputLen:n}=this;this.digestInto(e);let r=e.slice(0,n);return this.destroy(),r}_cloneInto(e){e||(e=new this.constructor),e.set(...this.get());let{blockLen:n,buffer:r,length:s,finished:o,destroyed:i,pos:a}=this;return e.destroyed=i,e.finished=o,e.length=s,e.pos=a,s%n&&e.buffer.set(r),e}clone(){return this._cloneInto()}},L=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]);var m=Uint32Array.from([1779033703,4089235720,3144134277,2227873595,1013904242,4271175723,2773480762,1595750129,1359893119,2917565137,2600822924,725511199,528734635,4215389547,1541459225,327033209]);var at=BigInt(4294967295),te=BigInt(32);function Ke(t,e=!1){return e?{h:Number(t&at),l:Number(t>>te&at)}:{h:Number(t>>te&at)|0,l:Number(t&at)|0}}function ee(t,e=!1){let n=t.length,r=new Uint32Array(n),s=new Uint32Array(n);for(let o=0;o<n;o++){let{h:i,l:a}=Ke(t[o],e);[r[o],s[o]]=[i,a]}return[r,s]}var Et=(t,e,n)=>t>>>n,Ut=(t,e,n)=>t<<32-n|e>>>n,G=(t,e,n)=>t>>>n|e<<32-n,M=(t,e,n)=>t<<32-n|e>>>n,Q=(t,e,n)=>t<<64-n|e>>>n-32,tt=(t,e,n)=>t>>>n-32|e<<64-n;function U(t,e,n,r){let s=(e>>>0)+(r>>>0);return{h:t+n+(s/2**32|0)|0,l:s|0}}var ne=(t,e,n)=>(t>>>0)+(e>>>0)+(n>>>0),se=(t,e,n,r)=>e+n+r+(t/2**32|0)|0,re=(t,e,n,r)=>(t>>>0)+(e>>>0)+(n>>>0)+(r>>>0),oe=(t,e,n,r,s)=>e+n+r+s+(t/2**32|0)|0,ie=(t,e,n,r,s)=>(t>>>0)+(e>>>0)+(n>>>0)+(r>>>0)+(s>>>0),ce=(t,e,n,r,s,o)=>e+n+r+s+o+(t/2**32|0)|0;var Xe=Uint32Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),k=new Uint32Array(64),ft=class extends W{constructor(e=32){super(64,e,8,!1),this.A=L[0]|0,this.B=L[1]|0,this.C=L[2]|0,this.D=L[3]|0,this.E=L[4]|0,this.F=L[5]|0,this.G=L[6]|0,this.H=L[7]|0}get(){let{A:e,B:n,C:r,D:s,E:o,F:i,G:a,H:c}=this;return[e,n,r,s,o,i,a,c]}set(e,n,r,s,o,i,a,c){this.A=e|0,this.B=n|0,this.C=r|0,this.D=s|0,this.E=o|0,this.F=i|0,this.G=a|0,this.H=c|0}process(e,n){for(let f=0;f<16;f++,n+=4)k[f]=e.getUint32(n,!1);for(let f=16;f<64;f++){let d=k[f-15],p=k[f-2],y=_(d,7)^_(d,18)^d>>>3,b=_(p,17)^_(p,19)^p>>>10;k[f]=b+k[f-7]+y+k[f-16]|0}let{A:r,B:s,C:o,D:i,E:a,F:c,G:u,H:l}=this;for(let f=0;f<64;f++){let d=_(a,6)^_(a,11)^_(a,25),p=l+d+Wt(a,c,u)+Xe[f]+k[f]|0,b=(_(r,2)^_(r,13)^_(r,22))+Qt(r,s,o)|0;l=u,u=c,c=a,a=i+p|0,i=o,o=s,s=r,r=p+b|0}r=r+this.A|0,s=s+this.B|0,o=o+this.C|0,i=i+this.D|0,a=a+this.E|0,c=c+this.F|0,u=u+this.G|0,l=l+this.H|0,this.set(r,s,o,i,a,c,u,l)}roundClean(){D(k)}destroy(){this.set(0,0,0,0,0,0,0,0),D(this.buffer)}};var ae=ee(["0x428a2f98d728ae22","0x7137449123ef65cd","0xb5c0fbcfec4d3b2f","0xe9b5dba58189dbbc","0x3956c25bf348b538","0x59f111f1b605d019","0x923f82a4af194f9b","0xab1c5ed5da6d8118","0xd807aa98a3030242","0x12835b0145706fbe","0x243185be4ee4b28c","0x550c7dc3d5ffb4e2","0x72be5d74f27b896f","0x80deb1fe3b1696b1","0x9bdc06a725c71235","0xc19bf174cf692694","0xe49b69c19ef14ad2","0xefbe4786384f25e3","0x0fc19dc68b8cd5b5","0x240ca1cc77ac9c65","0x2de92c6f592b0275","0x4a7484aa6ea6e483","0x5cb0a9dcbd41fbd4","0x76f988da831153b5","0x983e5152ee66dfab","0xa831c66d2db43210","0xb00327c898fb213f","0xbf597fc7beef0ee4","0xc6e00bf33da88fc2","0xd5a79147930aa725","0x06ca6351e003826f","0x142929670a0e6e70","0x27b70a8546d22ffc","0x2e1b21385c26c926","0x4d2c6dfc5ac42aed","0x53380d139d95b3df","0x650a73548baf63de","0x766a0abb3c77b2a8","0x81c2c92e47edaee6","0x92722c851482353b","0xa2bfe8a14cf10364","0xa81a664bbc423001","0xc24b8b70d0f89791","0xc76c51a30654be30","0xd192e819d6ef5218","0xd69906245565a910","0xf40e35855771202a","0x106aa07032bbd1b8","0x19a4c116b8d2d0c8","0x1e376c085141ab53","0x2748774cdf8eeb99","0x34b0bcb5e19b48a8","0x391c0cb3c5c95a63","0x4ed8aa4ae3418acb","0x5b9cca4f7763e373","0x682e6ff3d6b2b8a3","0x748f82ee5defb2fc","0x78a5636f43172f60","0x84c87814a1f0ab72","0x8cc702081a6439ec","0x90befffa23631e28","0xa4506cebde82bde9","0xbef9a3f7b2c67915","0xc67178f2e372532b","0xca273eceea26619c","0xd186b8c721c0c207","0xeada7dd6cde0eb1e","0xf57d4f7fee6ed178","0x06f067aa72176fba","0x0a637dc5a2c898a6","0x113f9804bef90dae","0x1b710b35131c471b","0x28db77f523047d84","0x32caab7b40c72493","0x3c9ebe0a15c9bebc","0x431d67c49c100d4c","0x4cc5d4becb3e42b6","0x597f299cfc657e2a","0x5fcb6fab3ad6faec","0x6c44198c4a475817"].map(t=>BigInt(t))),Ze=ae[0],Ye=ae[1],V=new Uint32Array(80),N=new Uint32Array(80),ut=class extends W{constructor(e=64){super(128,e,16,!1),this.Ah=m[0]|0,this.Al=m[1]|0,this.Bh=m[2]|0,this.Bl=m[3]|0,this.Ch=m[4]|0,this.Cl=m[5]|0,this.Dh=m[6]|0,this.Dl=m[7]|0,this.Eh=m[8]|0,this.El=m[9]|0,this.Fh=m[10]|0,this.Fl=m[11]|0,this.Gh=m[12]|0,this.Gl=m[13]|0,this.Hh=m[14]|0,this.Hl=m[15]|0}get(){let{Ah:e,Al:n,Bh:r,Bl:s,Ch:o,Cl:i,Dh:a,Dl:c,Eh:u,El:l,Fh:f,Fl:d,Gh:p,Gl:y,Hh:b,Hl:g}=this;return[e,n,r,s,o,i,a,c,u,l,f,d,p,y,b,g]}set(e,n,r,s,o,i,a,c,u,l,f,d,p,y,b,g){this.Ah=e|0,this.Al=n|0,this.Bh=r|0,this.Bl=s|0,this.Ch=o|0,this.Cl=i|0,this.Dh=a|0,this.Dl=c|0,this.Eh=u|0,this.El=l|0,this.Fh=f|0,this.Fl=d|0,this.Gh=p|0,this.Gl=y|0,this.Hh=b|0,this.Hl=g|0}process(e,n){for(let x=0;x<16;x++,n+=4)V[x]=e.getUint32(n),N[x]=e.getUint32(n+=4);for(let x=16;x<80;x++){let H=V[x-15]|0,E=N[x-15]|0,Y=G(H,E,1)^G(H,E,8)^Et(H,E,7),J=M(H,E,1)^M(H,E,8)^Ut(H,E,7),C=V[x-2]|0,I=N[x-2]|0,st=G(C,I,19)^Q(C,I,61)^Et(C,I,6),wt=M(C,I,19)^tt(C,I,61)^Ut(C,I,6),rt=re(J,wt,N[x-7],N[x-16]),_t=oe(rt,Y,st,V[x-7],V[x-16]);V[x]=_t|0,N[x]=rt|0}let{Ah:r,Al:s,Bh:o,Bl:i,Ch:a,Cl:c,Dh:u,Dl:l,Eh:f,El:d,Fh:p,Fl:y,Gh:b,Gl:g,Hh:B,Hl:$}=this;for(let x=0;x<80;x++){let H=G(f,d,14)^G(f,d,18)^Q(f,d,41),E=M(f,d,14)^M(f,d,18)^tt(f,d,41),Y=f&p^~f&b,J=d&y^~d&g,C=ie($,E,J,Ye[x],N[x]),I=ce(C,B,H,Y,Ze[x],V[x]),st=C|0,wt=G(r,s,28)^Q(r,s,34)^Q(r,s,39),rt=M(r,s,28)^tt(r,s,34)^tt(r,s,39),_t=r&o^r&a^o&a,Ge=s&i^s&c^i&c;B=b|0,$=g|0,b=p|0,g=y|0,p=f|0,y=d|0,{h:f,l:d}=U(u|0,l|0,I|0,st|0),u=a|0,l=c|0,a=o|0,c=i|0,o=r|0,i=s|0;let Yt=ne(st,rt,Ge);r=se(Yt,I,wt,_t),s=Yt|0}({h:r,l:s}=U(this.Ah|0,this.Al|0,r|0,s|0)),{h:o,l:i}=U(this.Bh|0,this.Bl|0,o|0,i|0),{h:a,l:c}=U(this.Ch|0,this.Cl|0,a|0,c|0),{h:u,l}=U(this.Dh|0,this.Dl|0,u|0,l|0),{h:f,l:d}=U(this.Eh|0,this.El|0,f|0,d|0),{h:p,l:y}=U(this.Fh|0,this.Fl|0,p|0,y|0),{h:b,l:g}=U(this.Gh|0,this.Gl|0,b|0,g|0),{h:B,l:$}=U(this.Hh|0,this.Hl|0,B|0,$|0),this.set(r,s,o,i,a,c,u,l,f,d,p,y,b,g,B,$)}roundClean(){D(V,N)}destroy(){D(this.buffer),this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)}};var fe=Ht(()=>new ft);var ue=Ht(()=>new ut);var ht=fe;var he={34:'\\"',92:"\\\\",8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"},S=class extends Error{constructor(e){super(e),this.name="CanonicalizationError"}};function le(t){let e='"';for(let n=0;n<t.length;n++){let r=t.charCodeAt(n);if(r<32){let s=he[r];s!==void 0?e+=s:e+="\\u"+r.toString(16).padStart(4,"0")}else r===34||r===92?e+=he[r]:e+=t[n]}return e+='"',e}function Je(t){if(typeof t!="number"||!Number.isFinite(t))throw new S(`CSC-1 forbids non-finite numbers; got ${t}`);if(!Number.isInteger(t))throw new S('CSC-1 forbids float in signed payloads; encode numeric parameters as strings (e.g. temperature="0.7")');if(t<-9007199254740991||t>9007199254740991)throw new S(`integer ${t} outside JS-safe range [${-9007199254740991}, ${9007199254740991}]; encode large integers as strings`);return String(t)}function We(t){return"["+t.map(n=>lt(n)).join(",")+"]"}function Qe(t,e){let n=Math.min(t.length,e.length);for(let r=0;r<n;r++){let s=t.charCodeAt(r),o=e.charCodeAt(r);if(s!==o)return s-o}return t.length-e.length}function tn(t){let e=Object.keys(t);for(let o of e)if(typeof o!="string")throw new S(`object key must be string, got ${typeof o}`);let n=new Set;for(let o of e){if(n.has(o))throw new S(`duplicate object key: ${o}`);n.add(o)}return"{"+[...e].sort(Qe).map(o=>le(o)+":"+lt(t[o])).join(",")+"}"}function lt(t){if(t===null)return"null";if(t===!0)return"true";if(t===!1)return"false";if(typeof t=="string")return le(t);if(typeof t=="number")return Je(t);if(typeof t=="bigint"){if(t<BigInt(-9007199254740991)||t>BigInt(9007199254740991))throw new S(`bigint ${t} outside JS-safe range; encode as string`);return t.toString()}if(Array.isArray(t))return We(t);if(typeof t=="object")return tn(t);throw t===void 0?new S("CSC-1 cannot serialize undefined"):new S(`CSC-1 cannot serialize value of type ${typeof t}`)}function j(t){let e=lt(t);return new TextEncoder().encode(e)}function en(t){return lt(t)}var de=globalThis;if(!de.crypto||typeof de.crypto.getRandomValues!="function")try{let t=await import("node:crypto");t.webcrypto?.getRandomValues&&(globalThis.crypto=t.webcrypto)}catch{}var nn={p:0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,n:0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,h:8n,a:0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,d:0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,Gx:0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,Gy:0x6666666666666666666666666666666666666666666666666666666666666658n},{p:A,n:dt,Gx:pe,Gy:xe,a:Rt,d:vt}=nn,sn=8n,v=32,et=64,w=(t="")=>{throw new Error(t)},rn=t=>typeof t=="bigint",we=t=>typeof t=="string",on=t=>t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name==="Uint8Array",K=(t,e)=>!on(t)||typeof e=="number"&&e>0&&t.length!==e?w("Uint8Array expected"):t,bt=t=>new Uint8Array(t),It=t=>Uint8Array.from(t),_e=(t,e)=>t.toString(16).padStart(e,"0"),Lt=t=>Array.from(K(t)).map(e=>_e(e,2)).join(""),F={_0:48,_9:57,A:65,F:70,a:97,f:102},ye=t=>{if(t>=F._0&&t<=F._9)return t-F._0;if(t>=F.A&&t<=F.F)return t-(F.A-10);if(t>=F.a&&t<=F.f)return t-(F.a-10)},Ft=t=>{let e="hex invalid";if(!we(t))return w(e);let n=t.length,r=n/2;if(n%2)return w(e);let s=bt(r);for(let o=0,i=0;o<r;o++,i+=2){let a=ye(t.charCodeAt(i)),c=ye(t.charCodeAt(i+1));if(a===void 0||c===void 0)return w(e);s[o]=a*16+c}return s},z=(t,e)=>K(we(t)?Ft(t):It(K(t)),e),Se=()=>globalThis?.crypto,cn=()=>Se()?.subtle??w("crypto.subtle must be defined"),nt=(...t)=>{let e=bt(t.reduce((r,s)=>r+K(s).length,0)),n=0;return t.forEach(r=>{e.set(r,n),n+=r.length}),e},Be=(t=v)=>Se().getRandomValues(bt(t)),pt=BigInt,q=(t,e,n,r="bad number: out of range")=>rn(t)&&e<=t&&t<n?t:w(r),h=(t,e=A)=>{let n=t%e;return n>=0n?n:e+n},He=t=>h(t,dt),Ee=(t,e)=>{(t===0n||e<=0n)&&w("no inverse n="+t+" mod="+e);let n=h(t,e),r=e,s=0n,o=1n,i=1n,a=0n;for(;n!==0n;){let c=r/n,u=r%n,l=s-i*c,f=o-a*c;r=n,n=u,s=i,o=a,i=l,a=f}return r===1n?h(s,e):w("no inverse")},an=t=>{let e=Z[t];return typeof e!="function"&&w("hashes."+t+" not set"),e},be=t=>t instanceof P?t:w("Point expected"),Tt=2n**256n,P=class t{static BASE;static ZERO;ex;ey;ez;et;constructor(e,n,r,s){let o=Tt;this.ex=q(e,0n,o),this.ey=q(n,0n,o),this.ez=q(r,1n,o),this.et=q(s,0n,o),Object.freeze(this)}static fromAffine(e){return new t(e.x,e.y,1n,h(e.x*e.y))}static fromBytes(e,n=!1){let r=vt,s=It(K(e,v)),o=e[31];s[31]=o&-129;let i=Pt(s);q(i,0n,n?Tt:A);let c=h(i*i),u=h(c-1n),l=h(r*c+1n),{isValid:f,value:d}=un(u,l);f||w("bad point: y not sqrt");let p=(d&1n)===1n,y=(o&128)!==0;return!n&&d===0n&&y&&w("bad point: x==0, isLastByteOdd"),y!==p&&(d=h(-d)),new t(d,i,1n,h(d*i))}assertValidity(){let e=Rt,n=vt,r=this;if(r.is0())throw new Error("bad point: ZERO");let{ex:s,ey:o,ez:i,et:a}=r,c=h(s*s),u=h(o*o),l=h(i*i),f=h(l*l),d=h(c*e),p=h(l*h(d+u)),y=h(f+h(n*h(c*u)));if(p!==y)throw new Error("bad point: equation left != right (1)");let b=h(s*o),g=h(i*a);if(b!==g)throw new Error("bad point: equation left != right (2)");return this}equals(e){let{ex:n,ey:r,ez:s}=this,{ex:o,ey:i,ez:a}=be(e),c=h(n*a),u=h(o*s),l=h(r*a),f=h(i*s);return c===u&&l===f}is0(){return this.equals(X)}negate(){return new t(h(-this.ex),this.ey,this.ez,h(-this.et))}double(){let{ex:e,ey:n,ez:r}=this,s=Rt,o=h(e*e),i=h(n*n),a=h(2n*h(r*r)),c=h(s*o),u=e+n,l=h(h(u*u)-o-i),f=c+i,d=f-a,p=c-i,y=h(l*d),b=h(f*p),g=h(l*p),B=h(d*f);return new t(y,b,B,g)}add(e){let{ex:n,ey:r,ez:s,et:o}=this,{ex:i,ey:a,ez:c,et:u}=be(e),l=Rt,f=vt,d=h(n*i),p=h(r*a),y=h(o*f*u),b=h(s*c),g=h((n+r)*(i+a)-d-p),B=h(b-y),$=h(b+y),x=h(p-l*d),H=h(g*B),E=h($*x),Y=h(g*x),J=h(B*$);return new t(H,E,J,Y)}multiply(e,n=!0){if(!n&&(e===0n||this.is0()))return X;if(q(e,1n,dt),e===1n)return this;if(this.equals(O))return yn(e).p;let r=X,s=O;for(let o=this;e>0n;o=o.double(),e>>=1n)e&1n?r=r.add(o):n&&(s=s.add(o));return r}toAffine(){let{ex:e,ey:n,ez:r}=this;if(this.equals(X))return{x:0n,y:1n};let s=Ee(r,A);return h(r*s)!==1n&&w("invalid inverse"),{x:h(e*s),y:h(n*s)}}toBytes(){let{x:e,y:n}=this.assertValidity().toAffine(),r=Ue(n);return r[31]|=e&1n?128:0,r}toHex(){return Lt(this.toBytes())}clearCofactor(){return this.multiply(pt(sn),!1)}isSmallOrder(){return this.clearCofactor().is0()}isTorsionFree(){let e=this.multiply(dt/2n,!1).double();return dt%2n&&(e=e.add(this)),e.is0()}static fromHex(e,n){return t.fromBytes(z(e),n)}get x(){return this.toAffine().x}get y(){return this.toAffine().y}toRawBytes(){return this.toBytes()}},O=new P(pe,xe,1n,h(pe*xe)),X=new P(0n,1n,1n,0n);P.BASE=O;P.ZERO=X;var Ue=t=>Ft(_e(q(t,0n,Tt),et)).reverse(),Pt=t=>pt("0x"+Lt(It(K(t)).reverse())),R=(t,e)=>{let n=t;for(;e-- >0n;)n*=n,n%=A;return n},fn=t=>{let n=t*t%A*t%A,r=R(n,2n)*n%A,s=R(r,1n)*t%A,o=R(s,5n)*s%A,i=R(o,10n)*o%A,a=R(i,20n)*i%A,c=R(a,40n)*a%A,u=R(c,80n)*c%A,l=R(u,80n)*c%A,f=R(l,10n)*o%A;return{pow_p_5_8:R(f,2n)*t%A,b2:n}},ge=0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n,un=(t,e)=>{let n=h(e*e*e),r=h(n*n*e),s=fn(t*r).pow_p_5_8,o=h(t*n*s),i=h(e*o*o),a=o,c=h(o*ge),u=i===t,l=i===h(-t),f=i===h(-t*ge);return u&&(o=a),(l||f)&&(o=c),(h(o)&1n)===1n&&(o=h(-o)),{isValid:u||l,value:o}},xt=t=>He(Pt(t)),Ot=(...t)=>Z.sha512Async(...t),hn=(...t)=>an("sha512Sync")(...t),Re=t=>{let e=t.slice(0,v);e[0]&=248,e[31]&=127,e[31]|=64;let n=t.slice(v,et),r=xt(e),s=O.multiply(r),o=s.toBytes();return{head:e,prefix:n,scalar:r,point:s,pointBytes:o}},$t=t=>Ot(z(t,v)).then(Re),ve=t=>Re(hn(z(t,v))),kt=t=>$t(t).then(e=>e.pointBytes),Te=t=>ve(t).pointBytes,Ce=t=>Ot(t.hashable).then(t.finish);var ln=(t,e,n)=>{let{pointBytes:r,scalar:s}=t,o=xt(e),i=O.multiply(o).toBytes();return{hashable:nt(i,r,n),finish:u=>{let l=He(o+xt(u)*s);return K(nt(i,Ue(l)),et)}}},Ie=async(t,e)=>{let n=z(t),r=await $t(e),s=await Ot(r.prefix,n);return Ce(ln(r,s,n))};var Le={zip215:!0},dn=(t,e,n,r=Le)=>{t=z(t,et),e=z(e),n=z(n,v);let{zip215:s}=r,o,i,a,c,u=Uint8Array.of();try{o=P.fromHex(n,s),i=P.fromHex(t.slice(0,v),s),a=Pt(t.slice(v,et)),c=O.multiply(a,!1),u=nt(i.toBytes(),o.toBytes(),e)}catch{}return{hashable:u,finish:f=>{if(c==null||!s&&o.isSmallOrder())return!1;let d=xt(f);return i.add(o.multiply(d,!1)).add(c.negate()).clearCofactor().is0()}}},Fe=async(t,e,n,r=Le)=>Ce(dn(t,e,n,r));var Z={sha512Async:async(...t)=>{let e=cn(),n=nt(...t);return bt(await e.digest("SHA-512",n.buffer))},sha512Sync:void 0,bytesToHex:Lt,hexToBytes:Ft,concatBytes:nt,mod:h,invert:Ee,randomBytes:Be},Vt={getExtendedPublicKeyAsync:$t,getExtendedPublicKey:ve,randomPrivateKey:()=>Be(v),precompute:(t=8,e=O)=>(e.multiply(3n),e)},yt=8,pn=256,Pe=Math.ceil(pn/yt)+1,Ct=2**(yt-1),xn=()=>{let t=[],e=O,n=e;for(let r=0;r<Pe;r++){n=e,t.push(n);for(let s=1;s<Ct;s++)n=n.add(e),t.push(n);e=n.double()}return t},me,Ae=(t,e)=>{let n=e.negate();return t?n:e},yn=t=>{let e=me||(me=xn()),n=X,r=O,s=2**yt,o=s,i=pt(s-1),a=pt(yt);for(let c=0;c<Pe;c++){let u=Number(t&i);t>>=a,u>Ct&&(u-=o,t+=1n);let l=c*Ct,f=l,d=l+Math.abs(u)-1,p=c%2!==0,y=u<0;u===0?r=r.add(Ae(p,e[f])):n=n.add(Ae(y,e[d]))}return{p:n,f:r}};var Oe=ue;Z.sha512Sync=(...t)=>Oe(Z.concatBytes(...t));async function gn(){let t=globalThis;if(t.crypto&&typeof t.crypto.getRandomValues=="function")return e=>t.crypto.getRandomValues(new Uint8Array(e));try{let e=await import("node:crypto");if(e.webcrypto?.getRandomValues)return n=>e.webcrypto.getRandomValues(new Uint8Array(n));if(typeof e.randomBytes=="function")return n=>new Uint8Array(e.randomBytes(n))}catch{}throw new Error("no secure RNG available \u2014 need WebCrypto or Node 'crypto' module")}var $e=await gn();Z.randomBytes=t=>$e(t??32);function ke(t){return $e(t)}var Ve=/^[0-9a-f]{64}$/;function T(t){let e="";for(let n=0;n<t.length;n++)e+=t[n].toString(16).padStart(2,"0");return e}function Nt(t){if(t.length%2!==0||!/^[0-9a-f]*$/.test(t))throw new Error(`invalid hex string of length ${t.length}`);let e=new Uint8Array(t.length/2);for(let n=0;n<e.length;n++)e[n]=parseInt(t.substring(n*2,n*2+2),16);return e}function Dt(t){if(!Ve.test(t))throw new Error(`expected 64 lowercase hex chars, got ${t.length} chars`);return Nt(t)}async function mn(){let t=Vt.randomPrivateKey(),e=await kt(t);return{privateHex:T(t),publicHex:T(e)}}function Gt(){let t=Vt.randomPrivateKey(),e=Te(t);return{privateHex:T(t),publicHex:T(e)}}async function An(t){let e=await kt(Dt(t));return T(e)}async function Mt(t,e){let n=await Ie(e,Dt(t));return T(n)}async function qt(t,e,n){if(e.length!==128||!/^[0-9a-f]{128}$/.test(e)||!Ve.test(t))return!1;try{return await Fe(Nt(e),n,Dt(t))}catch{return!1}}var Kt="crovia.receipt.v1",gt="CROVIA-RECEIPT-v1",zt=new TextEncoder().encode(gt+`
2
+ `),jt="csc-1",Xt="ed25519",Zt="sha256",Ne=/^cr_[0-9]{4}_[A-Z2-7]{26}$/;function wn(t=16){let e=ke(t),n="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",r="",s=0,o=0;for(let i=0;i<e.length;i++)for(o=o<<8|e[i],s+=8;s>=5;)s-=5,r+=n[o>>>s&31];return s>0&&(r+=n[o<<5-s&31]),r.slice(0,26)}function _n(){return`cr_${new Date().getUTCFullYear()}_${wn()}`}function Sn(){return new Date().toISOString()}function Bn(t){return"sha256:"+T(ht(t))}function mt(t){let e=j(t),n=new Uint8Array(zt.length+e.length);return n.set(zt,0),n.set(e,zt.length),n}function At(t){if(!t||typeof t!="object")return"receipt must be an object";let e=t;return e.v!==Kt?`v must be "${Kt}"`:typeof e.id!="string"||!Ne.test(e.id)?"id must match cr_YYYY_<26 base32 chars>":typeof e.issued_at!="string"||!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(e.issued_at)?"issued_at must be RFC 3339 UTC with ms precision":typeof e.payload_hash!="string"||!/^sha256:[0-9a-f]{64}$/.test(e.payload_hash)?"payload_hash must be 'sha256:<64 hex>'":e.payload_alg!==Zt?`payload_alg must be "${Zt}"`:e.payload_type!==void 0&&typeof e.payload_type!="string"?"payload_type must be string when present":e.prev!==null&&(typeof e.prev!="string"||!Ne.test(e.prev))?"prev must be null or a valid receipt id":typeof e.seq!="number"||!Number.isInteger(e.seq)||e.seq<0?"seq must be a non-negative integer":e.seq===0&&e.prev!==null?"seq=0 (genesis) requires prev=null":e.seq>0&&e.prev===null?"seq>0 requires prev to be a receipt id":typeof e.signer!="string"||!/^[0-9a-f]{64}$/.test(e.signer)?"signer must be 64 lowercase hex chars":e.sig_alg!==Xt?`sig_alg must be "${Xt}"`:e.canon!==jt?`canon must be "${jt}"`:e.domain!==gt?`domain must be "${gt}"`:typeof e.sig!="string"||!/^[0-9a-f]{128}$/.test(e.sig)?"sig must be 128 lowercase hex chars":null}async function Hn(t,e={}){let n=e.key??Gt(),r=j(t),s=Bn(r),o=e.prevReceipt?.id??null,i=e.prevReceipt?e.prevReceipt.seq+1:0,a=e.issuedAt??Sn(),c={v:Kt,id:_n(),issued_at:a,payload_hash:s,payload_alg:Zt,...e.payloadType!==void 0&&{payload_type:e.payloadType},prev:o,seq:i,signer:n.publicHex,sig_alg:Xt,canon:jt,domain:gt},u=mt(c),l=await Mt(n.privateHex,u),f={...c,sig:l},d=At(f);if(d!==null)throw new Error(`internal: produced invalid receipt \u2014 ${d}`);return f}function En(t){return"sha256:"+T(ht(t))}async function De(t,e){let n=[],r=At(t);if(r!==null)return n.push(`schema: ${r}`),{valid:!1,errors:n};let s=t,{sig:o,...i}=s,a=mt(i),c=!1;try{c=await qt(s.signer,o,a)}catch(u){return n.push(`signature-verify: ${u instanceof Error?u.message:String(u)}`),{valid:!1,errors:n}}if(!c)return n.push("signature: invalid"),{valid:!1,errors:n,id:s.id,signer:s.signer,payloadHash:s.payload_hash,prev:s.prev,seq:s.seq,issuedAt:s.issued_at};if(e!==void 0){let u;try{u=En(j(e))}catch(l){return n.push(`payload-canonicalize: ${l instanceof Error?l.message:String(l)}`),{valid:!1,errors:n}}if(u!==s.payload_hash)return n.push(`payload_hash mismatch: receipt=${s.payload_hash} computed=${u}`),{valid:!1,errors:n,id:s.id,signer:s.signer,payloadHash:s.payload_hash,prev:s.prev,seq:s.seq,issuedAt:s.issued_at}}return{valid:!0,errors:[],id:s.id,signer:s.signer,payloadHash:s.payload_hash,prev:s.prev,seq:s.seq,issuedAt:s.issued_at}}async function Un(t){if(t.length===0)return{valid:!1,errors:["empty chain"]};let e=null,n=-1,r=null;for(let s=0;s<t.length;s++){let o=t[s],i=await De(o);if(!i.valid)return{valid:!1,errors:[`chain[${s}] invalid: ${i.errors.join("; ")}`]};if(r===null)r=o.signer;else if(r!==o.signer)return{valid:!1,errors:[`chain[${s}] signer changed from ${r} to ${o.signer}`]};if(o.seq!==n+1)return{valid:!1,errors:[`chain[${s}] seq=${o.seq} but expected ${n+1}`]};if(o.prev!==e)return{valid:!1,errors:[`chain[${s}] prev=${o.prev} but expected ${e}`]};e=o.id,n=o.seq}return{valid:!0,errors:[],id:t[t.length-1].id,signer:r}}var Rn="https://croviatrust.com";async function vn(t,e={}){let n=e.endpoint??Rn,r=e.timeoutMs??1e4,s=e.fetch??globalThis.fetch;if(typeof s!="function")return{accepted:!1,status:0,error:"fetch is not available; pass opts.fetch"};let o=n.replace(/\/+$/,"")+"/api/anchor",i=new AbortController,a=setTimeout(()=>i.abort(),r);try{let c=await s(o,{method:"POST",headers:{"content-type":"application/json","user-agent":"crovia-seal/0.1.0"},body:JSON.stringify({receipt:t}),signal:i.signal}),u=null;try{u=await c.json()}catch{}let l=u;return c.ok?{accepted:!0,status:c.status,anchorId:l?.anchor_id}:{accepted:!1,status:c.status,error:l?.error??`HTTP ${c.status}`}}catch(c){return{accepted:!1,status:0,error:c instanceof Error?c.message:String(c)}}finally{clearTimeout(a)}}export{S as CanonicalizationError,j as canonicalize,en as canonicalizeString,mt as computePayload,mn as generateKey,Gt as generateKeySync,An as publicFromPrivate,vn as register,Hn as seal,Mt as signBytes,At as validateReceiptShape,De as verify,qt as verifyBytes,Un as verifyChain};
3
+ /*! Bundled license information:
4
+
5
+ @noble/hashes/esm/utils.js:
6
+ (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
7
+
8
+ @noble/ed25519/index.js:
9
+ (*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) *)
10
+ */
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Core types for @crovia/seal.
3
+ */
4
+ /** A signed continuity receipt over an arbitrary JSON payload. */
5
+ interface Receipt {
6
+ /** Format identifier. Always "crovia.receipt.v1" for this version. */
7
+ v: "crovia.receipt.v1";
8
+ /** Receipt identifier: "cr_YYYY_<26 base32 chars>". */
9
+ id: string;
10
+ /** RFC 3339 UTC timestamp with millisecond precision. */
11
+ issued_at: string;
12
+ /** "sha256:<64 lowercase hex>" of the canonical UTF-8 bytes of payload. */
13
+ payload_hash: string;
14
+ /** Hash algorithm used for payload_hash. Currently always "sha256". */
15
+ payload_alg: "sha256";
16
+ /** Optional content-type hint for the payload (free-form, not signed semantics). */
17
+ payload_type?: string;
18
+ /** Previous receipt id from the same signer, or null for genesis. */
19
+ prev: string | null;
20
+ /** Monotonic sequence per signer, 0 for genesis. */
21
+ seq: number;
22
+ /** Signer's Ed25519 public key as 64 lowercase hex chars. */
23
+ signer: string;
24
+ /** Signature algorithm: always "ed25519". */
25
+ sig_alg: "ed25519";
26
+ /** Canonicalization scheme: always "csc-1". */
27
+ canon: "csc-1";
28
+ /** Domain separator string baked into the signed payload. */
29
+ domain: "CROVIA-RECEIPT-v1";
30
+ /** Detached signature over compute_payload(receipt) — 128 lowercase hex. */
31
+ sig: string;
32
+ }
33
+ /** Options for `seal()`. */
34
+ interface SealOptions {
35
+ /** Bring-your-own Ed25519 key. If omitted, a fresh key is generated for this call. */
36
+ key?: KeyPair;
37
+ /** Previous receipt to chain against (provides prev + seq). */
38
+ prevReceipt?: Receipt;
39
+ /** Optional content-type hint (e.g., "text/plain", "application/json", "model-card"). */
40
+ payloadType?: string;
41
+ /** Override timestamp (testing only — must be RFC 3339 with ms precision). */
42
+ issuedAt?: string;
43
+ }
44
+ /** Ed25519 key pair, raw 32-byte private + 32-byte public. */
45
+ interface KeyPair {
46
+ /** 64 lowercase hex chars (32 bytes). */
47
+ privateHex: string;
48
+ /** 64 lowercase hex chars (32 bytes). */
49
+ publicHex: string;
50
+ }
51
+ /** Outcome of `verify()`. */
52
+ interface VerifyResult {
53
+ /** True iff structure, signature, and self-consistency all check out. */
54
+ valid: boolean;
55
+ /** Errors encountered (empty when valid=true). */
56
+ errors: string[];
57
+ /** Receipt id (echoed for convenience). */
58
+ id?: string;
59
+ /** Signer pubkey hex. */
60
+ signer?: string;
61
+ /** payload_hash echoed for the caller to compare against their payload. */
62
+ payloadHash?: string;
63
+ /** prev id, for chain walks. */
64
+ prev?: string | null;
65
+ /** sequence, for chain walks. */
66
+ seq?: number;
67
+ /** issued_at echoed for time-range checks. */
68
+ issuedAt?: string;
69
+ }
70
+ /** Options for `register()`. */
71
+ interface RegisterOptions {
72
+ /** Substrate URL. Defaults to https://croviatrust.com */
73
+ endpoint?: string;
74
+ /** Optional fetch override (for testing or custom transports). */
75
+ fetch?: typeof globalThis.fetch;
76
+ /** Request timeout in ms. Defaults to 10_000. */
77
+ timeoutMs?: number;
78
+ }
79
+ /** Outcome of `register()`. */
80
+ interface RegisterResult {
81
+ /** Whether the substrate accepted the receipt. */
82
+ accepted: boolean;
83
+ /** Substrate-assigned anchor id, if any. */
84
+ anchorId?: string;
85
+ /** HTTP status code returned by the substrate. */
86
+ status: number;
87
+ /** Error message if not accepted. */
88
+ error?: string;
89
+ }
90
+
91
+ /**
92
+ * Ed25519 key handling.
93
+ *
94
+ * @noble/ed25519 v2 ships only the curve operations and requires a
95
+ * SHA-512 implementation to be plugged in via `etc.sha512Sync`. We use
96
+ * @noble/hashes/sha512 for that.
97
+ */
98
+
99
+ /**
100
+ * Generate a fresh Ed25519 key pair.
101
+ * Uses crypto.getRandomValues — works in Node 18+ and modern browsers.
102
+ */
103
+ declare function generateKey(): Promise<KeyPair>;
104
+ /**
105
+ * Synchronous variant of generateKey() — usable when running with the
106
+ * sha512Sync hook installed (Node, Deno, modern browsers).
107
+ */
108
+ declare function generateKeySync(): KeyPair;
109
+ /** Derive the public hex from a private hex (does not mutate caller). */
110
+ declare function publicFromPrivate(privateHex: string): Promise<string>;
111
+ /**
112
+ * Sign raw bytes with an Ed25519 private key (hex).
113
+ * Returns 64-byte signature as 128 lowercase hex chars.
114
+ */
115
+ declare function signBytes(privateHex: string, message: Uint8Array): Promise<string>;
116
+ /** Verify a signature against bytes and a public key (all hex / Uint8Array). */
117
+ declare function verifyBytes(publicHex: string, signatureHex: string, message: Uint8Array): Promise<boolean>;
118
+
119
+ /**
120
+ * Compute the exact bytes that are signed.
121
+ * `payload` here is the receipt object minus its `sig` field.
122
+ */
123
+ declare function computePayload(receiptWithoutSig: Omit<Receipt, "sig">): Uint8Array;
124
+ /**
125
+ * Validate the structural shape of a receipt object.
126
+ * Returns `null` on success or an error string on failure.
127
+ */
128
+ declare function validateReceiptShape(r: unknown): string | null;
129
+ /**
130
+ * Produce a continuity receipt over an arbitrary JSON payload.
131
+ *
132
+ * Default behaviour: generates a fresh ephemeral key for this call.
133
+ * Pass `opts.key` to use your own key (recommended for chains).
134
+ */
135
+ declare function seal(payload: unknown, opts?: SealOptions): Promise<Receipt>;
136
+
137
+ /**
138
+ * Verify a continuity receipt.
139
+ *
140
+ * @param receipt The receipt object (or any value to be type-checked).
141
+ * @param payload Optional original payload to verify payload_hash against.
142
+ * If omitted, only the signature and structure are checked.
143
+ * @returns A VerifyResult — never throws.
144
+ */
145
+ declare function verify(receipt: unknown, payload?: unknown): Promise<VerifyResult>;
146
+ /**
147
+ * Verify a chain of receipts in order: each receipt[i].prev must equal
148
+ * receipt[i-1].id, sequence must increment by 1, and signer must remain
149
+ * the same. All signatures must verify.
150
+ *
151
+ * @returns true iff the chain is internally consistent and all sigs valid.
152
+ */
153
+ declare function verifyChain(receipts: Receipt[]): Promise<VerifyResult>;
154
+
155
+ /**
156
+ * `register()` — optional opt-in: post a receipt to the Crovia substrate.
157
+ *
158
+ * The substrate accepts the receipt, returns an anchor id and a position
159
+ * in the public continuity graph. This is OPTIONAL — `seal()` and
160
+ * `verify()` work fully offline. Calling `register()` is what causes the
161
+ * receipt to participate in the public substrate's continuity graph.
162
+ */
163
+
164
+ /**
165
+ * Register a receipt with the Crovia substrate.
166
+ *
167
+ * @returns A result describing whether the substrate accepted the receipt.
168
+ * Never throws on transport errors — they are returned as fields.
169
+ */
170
+ declare function register(receipt: Receipt, opts?: RegisterOptions): Promise<RegisterResult>;
171
+
172
+ /**
173
+ * CSC-1 — Crovia Seal Canonicalization v1.
174
+ *
175
+ * Strict subset of RFC 8785 (JCS):
176
+ * - Object keys sorted by UTF-16 code-unit order
177
+ * - String escapes per RFC 8259 (mandatory short escapes only)
178
+ * - Integers only (floats forbidden in signed payloads)
179
+ * - No insignificant whitespace
180
+ *
181
+ * This implementation MUST produce byte-identical output to the Python
182
+ * reference (`reference/python/crovia_seal/canonical.py`) for every shared
183
+ * conformance vector. Any divergence is a bug.
184
+ */
185
+ declare class CanonicalizationError extends Error {
186
+ constructor(message: string);
187
+ }
188
+ /**
189
+ * Canonicalize a JSON-compatible value to its UTF-8 byte representation.
190
+ * Output is byte-identical to the Python reference implementation.
191
+ */
192
+ declare function canonicalize(value: unknown): Uint8Array;
193
+ /**
194
+ * Canonicalize and return the string form (UTF-8 will be identical).
195
+ * Useful for debugging.
196
+ */
197
+ declare function canonicalizeString(value: unknown): string;
198
+
199
+ export { CanonicalizationError, type KeyPair, type Receipt, type RegisterOptions, type RegisterResult, type SealOptions, type VerifyResult, canonicalize, canonicalizeString, computePayload, generateKey, generateKeySync, publicFromPrivate, register, seal, signBytes, validateReceiptShape, verify, verifyBytes, verifyChain };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import{sha256 as Y}from"@noble/hashes/sha256";var U={34:'\\"',92:"\\\\",8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"},l=class extends Error{constructor(t){super(t),this.name="CanonicalizationError"}};function C(e){let t='"';for(let r=0;r<e.length;r++){let i=e.charCodeAt(r);if(i<32){let n=U[i];n!==void 0?t+=n:t+="\\u"+i.toString(16).padStart(4,"0")}else i===34||i===92?t+=U[i]:t+=e[r]}return t+='"',t}function M(e){if(typeof e!="number"||!Number.isFinite(e))throw new l(`CSC-1 forbids non-finite numbers; got ${e}`);if(!Number.isInteger(e))throw new l('CSC-1 forbids float in signed payloads; encode numeric parameters as strings (e.g. temperature="0.7")');if(e<-9007199254740991||e>9007199254740991)throw new l(`integer ${e} outside JS-safe range [${-9007199254740991}, ${9007199254740991}]; encode large integers as strings`);return String(e)}function K(e){return"["+e.map(r=>d(r)).join(",")+"]"}function V(e,t){let r=Math.min(e.length,t.length);for(let i=0;i<r;i++){let n=e.charCodeAt(i),s=t.charCodeAt(i);if(n!==s)return n-s}return e.length-t.length}function j(e){let t=Object.keys(e);for(let s of t)if(typeof s!="string")throw new l(`object key must be string, got ${typeof s}`);let r=new Set;for(let s of t){if(r.has(s))throw new l(`duplicate object key: ${s}`);r.add(s)}return"{"+[...t].sort(V).map(s=>C(s)+":"+d(e[s])).join(",")+"}"}function d(e){if(e===null)return"null";if(e===!0)return"true";if(e===!1)return"false";if(typeof e=="string")return C(e);if(typeof e=="number")return M(e);if(typeof e=="bigint"){if(e<BigInt(-9007199254740991)||e>BigInt(9007199254740991))throw new l(`bigint ${e} outside JS-safe range; encode as string`);return e.toString()}if(Array.isArray(e))return K(e);if(typeof e=="object")return j(e);throw e===void 0?new l("CSC-1 cannot serialize undefined"):new l(`CSC-1 cannot serialize value of type ${typeof e}`)}function y(e){let t=d(e);return new TextEncoder().encode(t)}function z(e){return d(e)}var N=globalThis;if(!N.crypto||typeof N.crypto.getRandomValues!="function")try{let e=await import("crypto");e.webcrypto?.getRandomValues&&(globalThis.crypto=e.webcrypto)}catch{}import*as o from"@noble/ed25519";import{sha512 as H}from"@noble/hashes/sha512";o.etc.sha512Sync=(...e)=>H(o.etc.concatBytes(...e));async function D(){let e=globalThis;if(e.crypto&&typeof e.crypto.getRandomValues=="function")return t=>e.crypto.getRandomValues(new Uint8Array(t));try{let t=await import("crypto");if(t.webcrypto?.getRandomValues)return r=>t.webcrypto.getRandomValues(new Uint8Array(r));if(typeof t.randomBytes=="function")return r=>new Uint8Array(t.randomBytes(r))}catch{}throw new Error("no secure RNG available \u2014 need WebCrypto or Node 'crypto' module")}var k=await D();o.etc.randomBytes=e=>k(e??32);function O(e){return k(e)}var q=/^[0-9a-f]{64}$/;function p(e){let t="";for(let r=0;r<e.length;r++)t+=e[r].toString(16).padStart(2,"0");return t}function w(e){if(e.length%2!==0||!/^[0-9a-f]*$/.test(e))throw new Error(`invalid hex string of length ${e.length}`);let t=new Uint8Array(e.length/2);for(let r=0;r<t.length;r++)t[r]=parseInt(e.substring(r*2,r*2+2),16);return t}function _(e){if(!q.test(e))throw new Error(`expected 64 lowercase hex chars, got ${e.length} chars`);return w(e)}async function J(){let e=o.utils.randomPrivateKey(),t=await o.getPublicKeyAsync(e);return{privateHex:p(e),publicHex:p(t)}}function A(){let e=o.utils.randomPrivateKey(),t=o.getPublicKey(e);return{privateHex:p(e),publicHex:p(t)}}async function L(e){let t=await o.getPublicKeyAsync(_(e));return p(t)}async function R(e,t){let r=await o.signAsync(t,_(e));return p(r)}async function S(e,t,r){if(t.length!==128||!/^[0-9a-f]{128}$/.test(t)||!q.test(e))return!1;try{return await o.verifyAsync(w(t),r,_(e))}catch{return!1}}var x="crovia.receipt.v1",m="CROVIA-RECEIPT-v1",v=new TextEncoder().encode(m+`
2
+ `),T="csc-1",$="ed25519",E="sha256",B=/^cr_[0-9]{4}_[A-Z2-7]{26}$/;function G(e=16){let t=O(e),r="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",i="",n=0,s=0;for(let c=0;c<t.length;c++)for(s=s<<8|t[c],n+=8;n>=5;)n-=5,i+=r[s>>>n&31];return n>0&&(i+=r[s<<5-n&31]),i.slice(0,26)}function X(){return`cr_${new Date().getUTCFullYear()}_${G()}`}function Z(){return new Date().toISOString()}function W(e){return"sha256:"+p(Y(e))}function h(e){let t=y(e),r=new Uint8Array(v.length+t.length);return r.set(v,0),r.set(t,v.length),r}function b(e){if(!e||typeof e!="object")return"receipt must be an object";let t=e;return t.v!==x?`v must be "${x}"`:typeof t.id!="string"||!B.test(t.id)?"id must match cr_YYYY_<26 base32 chars>":typeof t.issued_at!="string"||!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(t.issued_at)?"issued_at must be RFC 3339 UTC with ms precision":typeof t.payload_hash!="string"||!/^sha256:[0-9a-f]{64}$/.test(t.payload_hash)?"payload_hash must be 'sha256:<64 hex>'":t.payload_alg!==E?`payload_alg must be "${E}"`:t.payload_type!==void 0&&typeof t.payload_type!="string"?"payload_type must be string when present":t.prev!==null&&(typeof t.prev!="string"||!B.test(t.prev))?"prev must be null or a valid receipt id":typeof t.seq!="number"||!Number.isInteger(t.seq)||t.seq<0?"seq must be a non-negative integer":t.seq===0&&t.prev!==null?"seq=0 (genesis) requires prev=null":t.seq>0&&t.prev===null?"seq>0 requires prev to be a receipt id":typeof t.signer!="string"||!/^[0-9a-f]{64}$/.test(t.signer)?"signer must be 64 lowercase hex chars":t.sig_alg!==$?`sig_alg must be "${$}"`:t.canon!==T?`canon must be "${T}"`:t.domain!==m?`domain must be "${m}"`:typeof t.sig!="string"||!/^[0-9a-f]{128}$/.test(t.sig)?"sig must be 128 lowercase hex chars":null}async function Q(e,t={}){let r=t.key??A(),i=y(e),n=W(i),s=t.prevReceipt?.id??null,c=t.prevReceipt?t.prevReceipt.seq+1:0,g=t.issuedAt??Z(),a={v:x,id:X(),issued_at:g,payload_hash:n,payload_alg:E,...t.payloadType!==void 0&&{payload_type:t.payloadType},prev:s,seq:c,signer:r.publicHex,sig_alg:$,canon:T,domain:m},u=h(a),f=await R(r.privateHex,u),P={...a,sig:f},I=b(P);if(I!==null)throw new Error(`internal: produced invalid receipt \u2014 ${I}`);return P}import{sha256 as ee}from"@noble/hashes/sha256";function te(e){return"sha256:"+p(ee(e))}async function F(e,t){let r=[],i=b(e);if(i!==null)return r.push(`schema: ${i}`),{valid:!1,errors:r};let n=e,{sig:s,...c}=n,g=h(c),a=!1;try{a=await S(n.signer,s,g)}catch(u){return r.push(`signature-verify: ${u instanceof Error?u.message:String(u)}`),{valid:!1,errors:r}}if(!a)return r.push("signature: invalid"),{valid:!1,errors:r,id:n.id,signer:n.signer,payloadHash:n.payload_hash,prev:n.prev,seq:n.seq,issuedAt:n.issued_at};if(t!==void 0){let u;try{u=te(y(t))}catch(f){return r.push(`payload-canonicalize: ${f instanceof Error?f.message:String(f)}`),{valid:!1,errors:r}}if(u!==n.payload_hash)return r.push(`payload_hash mismatch: receipt=${n.payload_hash} computed=${u}`),{valid:!1,errors:r,id:n.id,signer:n.signer,payloadHash:n.payload_hash,prev:n.prev,seq:n.seq,issuedAt:n.issued_at}}return{valid:!0,errors:[],id:n.id,signer:n.signer,payloadHash:n.payload_hash,prev:n.prev,seq:n.seq,issuedAt:n.issued_at}}async function re(e){if(e.length===0)return{valid:!1,errors:["empty chain"]};let t=null,r=-1,i=null;for(let n=0;n<e.length;n++){let s=e[n],c=await F(s);if(!c.valid)return{valid:!1,errors:[`chain[${n}] invalid: ${c.errors.join("; ")}`]};if(i===null)i=s.signer;else if(i!==s.signer)return{valid:!1,errors:[`chain[${n}] signer changed from ${i} to ${s.signer}`]};if(s.seq!==r+1)return{valid:!1,errors:[`chain[${n}] seq=${s.seq} but expected ${r+1}`]};if(s.prev!==t)return{valid:!1,errors:[`chain[${n}] prev=${s.prev} but expected ${t}`]};t=s.id,r=s.seq}return{valid:!0,errors:[],id:e[e.length-1].id,signer:i}}var ne="https://croviatrust.com";async function se(e,t={}){let r=t.endpoint??ne,i=t.timeoutMs??1e4,n=t.fetch??globalThis.fetch;if(typeof n!="function")return{accepted:!1,status:0,error:"fetch is not available; pass opts.fetch"};let s=r.replace(/\/+$/,"")+"/api/anchor",c=new AbortController,g=setTimeout(()=>c.abort(),i);try{let a=await n(s,{method:"POST",headers:{"content-type":"application/json","user-agent":"crovia-seal/0.1.0"},body:JSON.stringify({receipt:e}),signal:c.signal}),u=null;try{u=await a.json()}catch{}let f=u;return a.ok?{accepted:!0,status:a.status,anchorId:f?.anchor_id}:{accepted:!1,status:a.status,error:f?.error??`HTTP ${a.status}`}}catch(a){return{accepted:!1,status:0,error:a instanceof Error?a.message:String(a)}}finally{clearTimeout(g)}}export{l as CanonicalizationError,y as canonicalize,z as canonicalizeString,h as computePayload,J as generateKey,A as generateKeySync,L as publicFromPrivate,se as register,Q as seal,R as signBytes,b as validateReceiptShape,F as verify,S as verifyBytes,re as verifyChain};
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@crovia/seal",
3
+ "version": "0.1.0",
4
+ "description": "Immutable continuity receipts for evolving AI systems. Offline cryptographic primitive — Ed25519 + canonical JSON, no network required.",
5
+ "license": "Apache-2.0",
6
+ "author": "Crovia Trust <https://croviatrust.com>",
7
+ "homepage": "https://croviatrust.com/seal",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/croviatrust/crovia-seal.git",
11
+ "directory": "sdk/javascript"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/croviatrust/crovia-seal/issues"
15
+ },
16
+ "keywords": [
17
+ "crovia",
18
+ "seal",
19
+ "continuity",
20
+ "receipt",
21
+ "ed25519",
22
+ "provenance",
23
+ "canonicalization",
24
+ "ai",
25
+ "transparency",
26
+ "csc-1",
27
+ "rfc-8785"
28
+ ],
29
+ "type": "module",
30
+ "main": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "browser": "./dist/browser.js",
36
+ "import": "./dist/index.js"
37
+ },
38
+ "./browser": {
39
+ "types": "./dist/index.d.ts",
40
+ "import": "./dist/browser.js"
41
+ }
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "src",
46
+ "README.md",
47
+ "LICENSE"
48
+ ],
49
+ "scripts": {
50
+ "build": "tsup src/index.ts --format esm --dts --clean --minify --target=es2022 && esbuild src/index.ts --bundle --format=esm --target=es2022 --minify --outfile=dist/browser.js --platform=browser --log-level=warning",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest",
53
+ "size": "npm run build && du -sh dist/*",
54
+ "prepublishOnly": "npm run test && npm run build"
55
+ },
56
+ "dependencies": {
57
+ "@noble/ed25519": "^2.1.0",
58
+ "@noble/hashes": "^1.4.0"
59
+ },
60
+ "devDependencies": {
61
+ "esbuild": "^0.24.0",
62
+ "@types/node": "^20.11.0",
63
+ "tsup": "^8.0.0",
64
+ "typescript": "^5.3.0",
65
+ "vitest": "^1.2.0"
66
+ },
67
+ "engines": {
68
+ "node": ">=18"
69
+ },
70
+ "publishConfig": {
71
+ "access": "public"
72
+ }
73
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Crypto polyfill — MUST be imported before any module that captures
3
+ * `globalThis.crypto` at load time (notably @noble/ed25519 v2.x).
4
+ *
5
+ * ESM modules are evaluated depth-first in declaration order, so a
6
+ * top-level `import "./_polyfill.js"` before the noble imports gives
7
+ * us a deterministic ordering.
8
+ *
9
+ * In browsers and modern Node (19+), this is a no-op because
10
+ * globalThis.crypto already exists. In Node 18.x ESM, we install
11
+ * webcrypto from "node:crypto" via top-level await.
12
+ */
13
+ const g = globalThis as {
14
+ crypto?: { getRandomValues?: (a: Uint8Array) => Uint8Array };
15
+ };
16
+
17
+ if (!g.crypto || typeof g.crypto.getRandomValues !== "function") {
18
+ try {
19
+ const nc = (await import("node:crypto")) as unknown as {
20
+ webcrypto?: { getRandomValues: (a: Uint8Array) => Uint8Array };
21
+ };
22
+ if (nc.webcrypto?.getRandomValues) {
23
+ (globalThis as { crypto?: unknown }).crypto = nc.webcrypto;
24
+ }
25
+ } catch {
26
+ // Not Node — leave whatever is there. If both browser and Node fail,
27
+ // noble-ed25519 will throw with its own clear error.
28
+ }
29
+ }
30
+
31
+ export {};