@@ -4,6 +4,7 @@ const test = require('node:test');
44const assert = require ( 'node:assert/strict' ) ;
55const fs = require ( 'node:fs' ) ;
66const path = require ( 'node:path' ) ;
7+ const { webcrypto } = require ( 'node:crypto' ) ;
78
89const handler = require ( '../api/verify' ) ;
910
@@ -18,6 +19,34 @@ function makeRes() {
1819 } ;
1920}
2021
22+ const subtle = webcrypto . subtle ;
23+
24+ function canonicalize ( value ) {
25+ if ( value === null || typeof value !== 'object' ) return JSON . stringify ( value ) ;
26+ if ( Array . isArray ( value ) ) return `[${ value . map ( canonicalize ) . join ( ',' ) } ]` ;
27+ return `{${ Object . keys ( value ) . sort ( ) . map ( ( key ) => `${ JSON . stringify ( key ) } :${ canonicalize ( value [ key ] ) } ` ) . join ( ',' ) } }` ;
28+ }
29+
30+ async function makeRuntimeReceipt ( ) {
31+ const keyPair = await subtle . generateKey ( { name : 'Ed25519' } , true , [ 'sign' , 'verify' ] ) ;
32+ const rawPub = Buffer . from ( await subtle . exportKey ( 'raw' , keyPair . publicKey ) ) . toString ( 'base64' ) ;
33+ const receipt = {
34+ signer : 'runtime.commandlayer.eth' ,
35+ verb : 'agent.execute' ,
36+ ts : '2026-05-20T00:00:00.000Z' ,
37+ input : { task : 'verify' , content : 'canonical' } ,
38+ output : { ok : true } ,
39+ execution : { runtime : 'prod' , run_id : 'run_1' } ,
40+ } ;
41+ const payload = { signer : receipt . signer , verb : receipt . verb , input : receipt . input , output : receipt . output , execution : receipt . execution , ts : receipt . ts } ;
42+ const canonical = canonicalize ( payload ) ;
43+ const digest = await subtle . digest ( 'SHA-256' , new TextEncoder ( ) . encode ( canonical ) ) ;
44+ const hashHex = Buffer . from ( digest ) . toString ( 'hex' ) ;
45+ const sigBytes = await subtle . sign ( { name : 'Ed25519' } , keyPair . privateKey , new TextEncoder ( ) . encode ( hashHex ) ) ;
46+ receipt . metadata = { proof : { canonicalization : 'json.sorted_keys.v1' , hash : { alg : 'SHA-256' , value : hashHex } , signature : { alg : 'Ed25519' , kid : 'vC4WbcNoq2znSCiQ' , value : Buffer . from ( sigBytes ) . toString ( 'base64' ) } , signer_id : 'runtime.commandlayer.eth' } } ;
47+ return { receipt, rawPub } ;
48+ }
49+
2150const sampleReceipt = JSON . parse (
2251 fs . readFileSync ( path . join ( __dirname , 'fixtures' , 'canonical-receipt.sample.json' ) , 'utf8' )
2352) ;
@@ -94,3 +123,28 @@ test('POST /api/verify oversized body => 413', async () => {
94123 assert . equal ( res . body . ok , false ) ;
95124 assert . equal ( res . body . status , 'INVALID' ) ;
96125} ) ;
126+
127+
128+ test ( 'POST /api/verify can verify with injected ENS resolver' , async ( ) => {
129+ const { receipt, rawPub } = await makeRuntimeReceipt ( ) ;
130+ const req = {
131+ method : 'POST' ,
132+ body : receipt ,
133+ verifyOptions : {
134+ ens : {
135+ textResolver : async ( _ens , key ) => ( {
136+ 'cl.sig.pub' : `ed25519:${ rawPub } ` ,
137+ 'cl.sig.kid' : 'vC4WbcNoq2znSCiQ' ,
138+ 'cl.sig.canonical' : 'json.sorted_keys.v1' ,
139+ 'cl.receipt.signer' : 'runtime.commandlayer.eth' ,
140+ } ) [ key ] || null ,
141+ } ,
142+ } ,
143+ } ;
144+ const res = makeRes ( ) ;
145+ await handler ( req , res ) ;
146+ assert . equal ( res . statusCode , 200 ) ;
147+ assert . equal ( res . body . status , 'VERIFIED' ) ;
148+ assert . equal ( res . body . public_key_source , 'ens_txt' ) ;
149+ assert . equal ( res . body . ens_resolved , true ) ;
150+ } ) ;
0 commit comments