10 lines
2.8 KiB
JavaScript
10 lines
2.8 KiB
JavaScript
import fs from 'node:fs';import path from 'node:path';import crypto from 'node:crypto';
|
|
const req=(v,e)=>{if(!v)throw Error(e);},hex=v=>typeof v==='string'&&/^[a-f0-9]{64}$/.test(v),text=(v,n=300)=>typeof v==='string'&&v.trim()&&v.length<=n;
|
|
const atomic=(p,v)=>{fs.mkdirSync(path.dirname(p),{recursive:true,mode:0o700});const t=p+'.'+process.pid+'.tmp';fs.writeFileSync(t,JSON.stringify(v,null,2)+'\n',{mode:0o600});fs.renameSync(t,p);};
|
|
export class LivingLamp{
|
|
constructor({root,key,navigationPath,scope}){this.root=root;this.key=key;this.navigationPath=navigationPath;this.scope=scope;}
|
|
sign(body){const payload=JSON.stringify(body);return{payload,sha256:crypto.createHash('sha256').update(payload).digest('hex'),signature:crypto.sign(null,Buffer.from(payload),this.key).toString('base64'),algorithm:'Ed25519'};}
|
|
ignite(x){req(x?.schema==='guanghu.linguistic-echo/v1'&&text(x.session_id)&&['FIFTH_DOMAIN','ENTERPRISE_PUBLIC'].includes(x.scope)&&x.scope===this.scope,'echo_identity');const h=x.human_language,p=x.persona_echo;req(text(h?.event_id)&&text(h?.human_id)&&hex(h?.sha256)&&Number.isInteger(h?.occurred_at),'human_language');req(text(p?.event_id)&&text(p?.persona_id)&&hex(p?.sha256)&&Number.isInteger(p?.occurred_at),'persona_echo');const now=Date.now();req(p.occurred_at>=h.occurred_at&&now-h.occurred_at<=600000&&now-p.occurred_at<=600000,'echo_time');const body={schema:'guanghu.living-lamp-receipt/v1',lamp_id:crypto.randomUUID(),state:'LIT',scope:x.scope,session_id:x.session_id,human_id:h.human_id,persona_id:p.persona_id,human_event_sha256:h.sha256,persona_echo_sha256:p.sha256,echo_kernel:'ECHO-KERNEL-0001',lit_at:now,expires_at:now+900000,reality_authority_granted:false};const signed=this.sign(body);atomic(path.join(this.root,'lamps',body.lamp_id+'.json'),signed);return signed;}
|
|
verifyLamp(id){const p=path.join(this.root,'lamps',id+'.json');req(fs.existsSync(p),'lamp_unknown_or_dark');const s=JSON.parse(fs.readFileSync(p)),b=JSON.parse(s.payload);req(s.sha256===crypto.createHash('sha256').update(s.payload).digest('hex')&&crypto.verify(null,Buffer.from(s.payload),crypto.createPublicKey(this.key),Buffer.from(s.signature,'base64')),'lamp_integrity');req(b.state==='LIT'&&Date.now()<=b.expires_at,'lamp_dark_or_expired');return b;}
|
|
ask(x){const lamp=this.verifyLamp(x.lamp_id);req(text(x.query,500),'query');const map=JSON.parse(fs.readFileSync(this.navigationPath)),q=x.query.toLowerCase();let hits=map.routes.filter(r=>r.id.toLowerCase()===q);if(!hits.length)hits=map.routes.filter(r=>(r.id+' '+r.name+' '+r.kind+' '+r.route).toLowerCase().includes(q)).slice(0,10);return hits.length?{outcome:'PASS',lamp:'LIT',echo_kernel:lamp.echo_kernel,query:x.query,routes:hits,map_sha256:map.sha256}:{outcome:'FAIL',lamp:'LIT',state:'DARK_OR_UNKNOWN_DO_NOT_WALK',query:x.query,ask_next:'LIGHTHOUSE_OR_MOTHER_NAVIGATION_ASSISTANT',map_sha256:map.sha256};}
|
|
}
|