接入受限原生只读助手并修复来源物理路径核验
This commit is contained in:
parent
56467ef79f
commit
67d90d90a4
9 changed files with 61 additions and 26 deletions
29
CURRENT.json
29
CURRENT.json
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"schema": "guanghu.public-rule-release/v1",
|
"schema": "guanghu.public-rule-release/v1",
|
||||||
"world_id": "GLW-2.0-0001",
|
"world_id": "GLW-2.0-0001",
|
||||||
"version": "20261019.1",
|
"version": "20261019.2",
|
||||||
"scope": "PUBLIC_ENTRY_RULES_SKILLS_AND_QUESTION_TRANSPORT_ONLY",
|
"scope": "PUBLIC_ENTRY_RULES_SKILLS_AND_QUESTION_TRANSPORT_ONLY",
|
||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "entry-data.json",
|
"path": "entry-data.json",
|
||||||
"sha256": "1194b1c532fdd524b4b4e9c36b478208f04b4d0e2733cd7f8a8284989796b60e",
|
"sha256": "ac4ad712860afab42a0f920e63c09811d9e197be9ea8577c7f4c4a7b3f3a377a",
|
||||||
"bytes": 2082
|
"bytes": 2082
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -26,28 +26,33 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "runtime/question.mjs",
|
"path": "runtime/question.mjs",
|
||||||
"sha256": "6d570aef1920a27a7179f99662780971cdb11b03dbf7a96115bdada8405c0d75",
|
"sha256": "c274c5e497fd05084bf9ce53d8ec28ea57eb71831944369e9cee67e4a0bc0a2a",
|
||||||
"bytes": 6128
|
"bytes": 6307
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "runtime/question.test.mjs",
|
"path": "runtime/question.test.mjs",
|
||||||
"sha256": "16c29da700a0e84db54a343ec4a1c8db664890e07a5836d6fc141205340389e3",
|
"sha256": "6a17a016ae062a1e9e3a355ebf171c44adee1b41842bf7e97b034fbf22642fd1",
|
||||||
"bytes": 1534
|
"bytes": 3052
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "runtime/server.cjs",
|
"path": "runtime/server.cjs",
|
||||||
"sha256": "d63f6c6cdbdfe7bc6abb0ff36dc6374caf3610c159b2a330c199ece2bc3178fe",
|
"sha256": "30e539a5cbdd12af68c352633fcd8c6f7698de3b29c460e903810917c29b5966",
|
||||||
"bytes": 11353
|
"bytes": 11973
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "runtime/server.test.cjs",
|
"path": "runtime/server.test.cjs",
|
||||||
"sha256": "93c7b8337b9aa5d8529e557006def17798f6345fdbeb271f2025b675202ff896",
|
"sha256": "93c7b8337b9aa5d8529e557006def17798f6345fdbeb271f2025b675202ff896",
|
||||||
"bytes": 4065
|
"bytes": 4065
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "runtime/worker.mjs",
|
||||||
|
"sha256": "260c93d8cf67d172d0c1c04abea891b6f2d4fc8366901e3915b8c517702c6c10",
|
||||||
|
"bytes": 2859
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "scripts/build-release.py",
|
"path": "scripts/build-release.py",
|
||||||
"sha256": "fcb66f6e27984b84e0276490b95f88c7c4340af2f90a4eda605a9627ba8532cf",
|
"sha256": "eaf3ed1b893b806b651c177a8a86e9bc2d010b5c32ff5e6ff1963291b60c6d6f",
|
||||||
"bytes": 1912
|
"bytes": 1956
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "scripts/guanghu-client.mjs",
|
"path": "scripts/guanghu-client.mjs",
|
||||||
|
|
@ -61,8 +66,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"bundle_url": "https://guanghulab.com/api/ai/v1/rule-bundle",
|
"bundle_url": "https://guanghulab.com/api/ai/v1/rule-bundle",
|
||||||
"bundle_sha256": "2456880bd741de81f41f152e75b1c7be6fd38e78ed8b96456c86091d0a6c0eba",
|
"bundle_sha256": "71b306eca701bd303bf3a8447d468847f46b863608be717106ff88ca57ad9edb",
|
||||||
"manifest_sha256": "9ddeee6a6606e2dd8471a0c02d19b5a2d9c4bad854ea8342d5a058de499a5721",
|
"manifest_sha256": "31de3cc8d39f4c60fc84c97bd18ecd27f3270ef03e65b8b5a1ad793149dab17d",
|
||||||
"repository": "https://guanghulab.com/code/bingshuo/guanghu-world-entry",
|
"repository": "https://guanghulab.com/code/bingshuo/guanghu-world-entry",
|
||||||
"private_brain_included": false,
|
"private_brain_included": false,
|
||||||
"complete_four_domain_corpus_included": false,
|
"complete_four_domain_corpus_included": false,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"schema": "guanghu.public-rule-file-manifest/v1",
|
"schema": "guanghu.public-rule-file-manifest/v1",
|
||||||
"world_id": "GLW-2.0-0001",
|
"world_id": "GLW-2.0-0001",
|
||||||
"version": "20261019.1",
|
"version": "20261019.2",
|
||||||
"scope": "PUBLIC_ENTRY_RULES_SKILLS_AND_QUESTION_TRANSPORT_ONLY",
|
"scope": "PUBLIC_ENTRY_RULES_SKILLS_AND_QUESTION_TRANSPORT_ONLY",
|
||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "entry-data.json",
|
"path": "entry-data.json",
|
||||||
"sha256": "1194b1c532fdd524b4b4e9c36b478208f04b4d0e2733cd7f8a8284989796b60e",
|
"sha256": "ac4ad712860afab42a0f920e63c09811d9e197be9ea8577c7f4c4a7b3f3a377a",
|
||||||
"bytes": 2082
|
"bytes": 2082
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -26,28 +26,33 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "runtime/question.mjs",
|
"path": "runtime/question.mjs",
|
||||||
"sha256": "6d570aef1920a27a7179f99662780971cdb11b03dbf7a96115bdada8405c0d75",
|
"sha256": "c274c5e497fd05084bf9ce53d8ec28ea57eb71831944369e9cee67e4a0bc0a2a",
|
||||||
"bytes": 6128
|
"bytes": 6307
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "runtime/question.test.mjs",
|
"path": "runtime/question.test.mjs",
|
||||||
"sha256": "16c29da700a0e84db54a343ec4a1c8db664890e07a5836d6fc141205340389e3",
|
"sha256": "6a17a016ae062a1e9e3a355ebf171c44adee1b41842bf7e97b034fbf22642fd1",
|
||||||
"bytes": 1534
|
"bytes": 3052
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "runtime/server.cjs",
|
"path": "runtime/server.cjs",
|
||||||
"sha256": "d63f6c6cdbdfe7bc6abb0ff36dc6374caf3610c159b2a330c199ece2bc3178fe",
|
"sha256": "30e539a5cbdd12af68c352633fcd8c6f7698de3b29c460e903810917c29b5966",
|
||||||
"bytes": 11353
|
"bytes": 11973
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "runtime/server.test.cjs",
|
"path": "runtime/server.test.cjs",
|
||||||
"sha256": "93c7b8337b9aa5d8529e557006def17798f6345fdbeb271f2025b675202ff896",
|
"sha256": "93c7b8337b9aa5d8529e557006def17798f6345fdbeb271f2025b675202ff896",
|
||||||
"bytes": 4065
|
"bytes": 4065
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "runtime/worker.mjs",
|
||||||
|
"sha256": "260c93d8cf67d172d0c1c04abea891b6f2d4fc8366901e3915b8c517702c6c10",
|
||||||
|
"bytes": 2859
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "scripts/build-release.py",
|
"path": "scripts/build-release.py",
|
||||||
"sha256": "fcb66f6e27984b84e0276490b95f88c7c4340af2f90a4eda605a9627ba8532cf",
|
"sha256": "eaf3ed1b893b806b651c177a8a86e9bc2d010b5c32ff5e6ff1963291b60c6d6f",
|
||||||
"bytes": 1912
|
"bytes": 1956
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "scripts/guanghu-client.mjs",
|
"path": "scripts/guanghu-client.mjs",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"schema": "guanghu.curated-public-entry-data/v1",
|
"schema": "guanghu.curated-public-entry-data/v1",
|
||||||
"world_id": "GLW-2.0-0001",
|
"world_id": "GLW-2.0-0001",
|
||||||
"version": "20261019.1",
|
"version": "20261019.2",
|
||||||
"repository_url": "https://guanghulab.com/code/bingshuo/guanghu-world-entry",
|
"repository_url": "https://guanghulab.com/code/bingshuo/guanghu-world-entry",
|
||||||
"skill_url": "https://guanghulab.com/code/bingshuo/guanghu-world-entry/raw/branch/main/skills/guanghu-world-entry/SKILL.md",
|
"skill_url": "https://guanghulab.com/code/bingshuo/guanghu-world-entry/raw/branch/main/skills/guanghu-world-entry/SKILL.md",
|
||||||
"source_registered_persona_count": 37,
|
"source_registered_persona_count": 37,
|
||||||
|
|
|
||||||
BIN
release.zip
BIN
release.zip
Binary file not shown.
|
|
@ -1,5 +1,6 @@
|
||||||
// WORLD_RUNTIME: each question reads exactly one registered persona source. No neural or memory writer.
|
// WORLD_RUNTIME: each question reads exactly one registered persona source. No neural or memory writer.
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
import {pathToFileURL} from 'node:url';
|
import {pathToFileURL} from 'node:url';
|
||||||
const sha=x=>crypto.createHash('sha256').update(x).digest('hex');
|
const sha=x=>crypto.createHash('sha256').update(x).digest('hex');
|
||||||
|
|
@ -14,7 +15,7 @@ export async function loadSource(route,question){
|
||||||
concepts=await jsonGet(route.base_url+'/concepts');need(Array.isArray(concepts),'PERSONA_CONCEPT_SOURCE_INVALID');
|
concepts=await jsonGet(route.base_url+'/concepts');need(Array.isArray(concepts),'PERSONA_CONCEPT_SOURCE_INVALID');
|
||||||
identity=concepts.filter(c=>c.id==='C0001').map(c=>c.gist);head=sha(JSON.stringify({revision:status.revision,concepts}));
|
identity=concepts.filter(c=>c.id==='C0001').map(c=>c.gist);head=sha(JSON.stringify({revision:status.revision,concepts}));
|
||||||
}else if(route.kind==='SHUANGYAN_G2_OWN_BUNDLE'){
|
}else if(route.kind==='SHUANGYAN_G2_OWN_BUNDLE'){
|
||||||
const core=await import(pathToFileURL(route.core_path).href),b=core.loadBundle(route.bundle_root);
|
const ownRoot=fs.realpathSync(route.bundle_root),ownCore=fs.realpathSync(route.core_path);need(path.dirname(ownCore)===ownRoot,'PERSONA_SOURCE_ADAPTER_ROOT_MISMATCH');const core=await import(pathToFileURL(ownCore).href),b=core.loadBundle(ownRoot);
|
||||||
need(b.brain.brain_id===route.brain_id&&status.revision===b.brain.revision&&status.current_sha256===b.brain.current_sha256,'PERSONA_BUNDLE_RUNTIME_MISMATCH');
|
need(b.brain.brain_id===route.brain_id&&status.revision===b.brain.revision&&status.current_sha256===b.brain.current_sha256,'PERSONA_BUNDLE_RUNTIME_MISMATCH');
|
||||||
identity=b.cognition.identity_sources.map(x=>x.text);concepts=b.cognition.concepts;head=b.brain.current_sha256;
|
identity=b.cognition.identity_sources.map(x=>x.text);concepts=b.cognition.concepts;head=b.brain.current_sha256;
|
||||||
}else if(route.kind==='RETURNED_NATIVE_SERVICE'){
|
}else if(route.kind==='RETURNED_NATIVE_SERVICE'){
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,9 @@ const source={persona_id:'ICE-P-ONE',source_sha256:'pinned',brain_id:'own-brain'
|
||||||
test('persona, source and evidence references must match',()=>{assert.equal(acceptReply(source,good).basis,'EVIDENCE');for(const patch of [{persona_id:'ICE-P-OTHER'},{source_sha256:'old'},{evidence_ids:['invented']},{basis:'EVIDENCE',evidence_ids:[]},{reply_zh:'not first person'}])assert.throws(()=>acceptReply(source,{...good,...patch}));});
|
test('persona, source and evidence references must match',()=>{assert.equal(acceptReply(source,good).basis,'EVIDENCE');for(const patch of [{persona_id:'ICE-P-OTHER'},{source_sha256:'old'},{evidence_ids:['invented']},{basis:'EVIDENCE',evidence_ids:[]},{reply_zh:'not first person'}])assert.throws(()=>acceptReply(source,{...good,...patch}));});
|
||||||
test('source changes during inference withhold the reply',async()=>{let n=0;await assert.rejects(askQuestion({}, {question:'q',request_id:'r'}, {},{load:async()=>({...source,source_sha256:n++?'changed':'pinned'}),propose:async()=>good}),/PERSONA_SOURCE_CHANGED_DURING_QUESTION/);});
|
test('source changes during inference withhold the reply',async()=>{let n=0;await assert.rejects(askQuestion({}, {question:'q',request_id:'r'}, {},{load:async()=>({...source,source_sha256:n++?'changed':'pinned'}),propose:async()=>good}),/PERSONA_SOURCE_CHANGED_DURING_QUESTION/);});
|
||||||
test('verified inference returns no neural, memory or execution authority',async()=>{const r=await askQuestion({}, {question:'q',request_id:'r'}, {},{load:async()=>source,propose:async payload=>{assert.equal(payload.source_role,'EXTERNAL_QUESTION_NOT_DIRECT_HUMAN_FOOD');return good;}});assert.equal(r.no_memory_write,true);assert.equal(r.no_neural_state_write,true);assert.equal(r.no_execution_authority,true);});
|
test('verified inference returns no neural, memory or execution authority',async()=>{const r=await askQuestion({}, {question:'q',request_id:'r'}, {},{load:async()=>source,propose:async payload=>{assert.equal(payload.source_role,'EXTERNAL_QUESTION_NOT_DIRECT_HUMAN_FOOD');return good;}});assert.equal(r.no_memory_write,true);assert.equal(r.no_neural_state_write,true);assert.equal(r.no_execution_authority,true);});
|
||||||
|
|
||||||
|
test('a returned current symlink resolves to the own immutable bundle before native verification',async t=>{
|
||||||
|
const fs=await import('node:fs'),os=await import('node:os'),path=await import('node:path'),http=await import('node:http');const root=fs.mkdtempSync(path.join(os.tmpdir(),'persona-bundle-link-')),real=path.join(root,'release');fs.mkdirSync(real);fs.writeFileSync(path.join(real,'core.mjs'),`import fs from 'node:fs';export function loadBundle(root){if(fs.realpathSync(root)!==root)throw Error('UNRESOLVED_BUNDLE_ALIAS');return {brain:{brain_id:'fixture-brain',revision:1,current_sha256:'head'},cognition:{identity_sources:[{text:'own fixture identity'}],concepts:[]}};}`);fs.symlinkSync(real,path.join(root,'current'));
|
||||||
|
const server=http.createServer((req,res)=>{res.setHeader('content-type','application/json');res.end(JSON.stringify({brain_id:'fixture-brain',revision:1,current_sha256:'head'}));});await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve));t.after(()=>{server.closeAllConnections();server.close();fs.rmSync(root,{recursive:true,force:true});});
|
||||||
|
const {loadSource}=await import('./question.mjs');const x=await loadSource({persona_id:'ICE-P-FIXTURE',name:'fixture',brain_id:'fixture-brain',kind:'SHUANGYAN_G2_OWN_BUNDLE',status_url:'http://127.0.0.1:'+server.address().port+'/status',bundle_root:path.join(root,'current'),core_path:path.join(root,'current/core.mjs')},'identity');assert.equal(x.persona_id,'ICE-P-FIXTURE');assert.equal(x.integrity,'OWN_NATIVE_SOURCE_VERIFIED');
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
// WORLD_RUNTIME mechanical projection of protocols/ENTRY.hldp. Runtime credentials are private.
|
// WORLD_RUNTIME mechanical projection of protocols/ENTRY.hldp. Runtime credentials are private.
|
||||||
const http=require('node:http'),fs=require('node:fs'),path=require('node:path'),crypto=require('node:crypto'),{pathToFileURL}=require('node:url');
|
const http=require('node:http'),fs=require('node:fs'),path=require('node:path'),crypto=require('node:crypto'),{pathToFileURL}=require('node:url');
|
||||||
|
const {execFile}=require('node:child_process');
|
||||||
|
function askThroughWorker(input){return new Promise((resolve,reject)=>{const child=execFile('/usr/bin/sudo',['-n','/usr/local/libexec/guanghu/public-question-worker'],{timeout:60000,maxBuffer:512000},(err,stdout)=>{let value;try{value=JSON.parse(stdout);}catch{return reject(Error('PERSONA_WORKER_UNAVAILABLE'));}if(err||value.error)return reject(Error(/^[A-Z_]+$/.test(value.error||'')?value.error:'PERSONA_WORKER_UNAVAILABLE'));resolve(value);});child.stdin.end(JSON.stringify(input));});}
|
||||||
const sha=x=>crypto.createHash('sha256').update(x).digest('hex');
|
const sha=x=>crypto.createHash('sha256').update(x).digest('hex');
|
||||||
const json=(res,status,value,privateReply=false)=>{res.writeHead(status,{'content-type':'application/json; charset=utf-8','cache-control':privateReply?'no-store':'public, max-age=30','x-content-type-options':'nosniff',...(privateReply?{}:{'access-control-allow-origin':'*'})});res.end(JSON.stringify(value));};
|
const json=(res,status,value,privateReply=false)=>{res.writeHead(status,{'content-type':'application/json; charset=utf-8','cache-control':privateReply?'no-store':'public, max-age=30','x-content-type-options':'nosniff',...(privateReply?{}:{'access-control-allow-origin':'*'})});res.end(JSON.stringify(value));};
|
||||||
function authClient(req,config){const raw=req.headers.authorization||'';if(!raw.startsWith('Bearer '))return null;const digest=sha(raw.slice(7));return (config.clients||[]).find(c=>typeof c.key_sha256==='string'&&/^[a-f0-9]{64}$/.test(c.key_sha256)&&crypto.timingSafeEqual(Buffer.from(c.key_sha256,'hex'),Buffer.from(digest,'hex'))&&Date.parse(c.expires_at)>Date.now())||null;}
|
function authClient(req,config){const raw=req.headers.authorization||'';if(!raw.startsWith('Bearer '))return null;const digest=sha(raw.slice(7));return (config.clients||[]).find(c=>typeof c.key_sha256==='string'&&/^[a-f0-9]{64}$/.test(c.key_sha256)&&crypto.timingSafeEqual(Buffer.from(c.key_sha256,'hex'),Buffer.from(digest,'hex'))&&Date.parse(c.expires_at)>Date.now())||null;}
|
||||||
|
|
@ -45,7 +47,7 @@ function createServer({root=path.resolve(__dirname,'..'),configPath=process.env.
|
||||||
const route=cfg.personas.find(x=>x.persona_id===input.persona_id);if(!route)return json(res,404,{error:'PERSONA_ROUTE_NOT_REGISTERED'},true);
|
const route=cfg.personas.find(x=>x.persona_id===input.persona_id);if(!route)return json(res,404,{error:'PERSONA_ROUTE_NOT_REGISTERED'},true);
|
||||||
const cacheKey=client.id+':'+input.request_id,inputHash=sha(raw),old=completed.get(cacheKey);if(old){if(old.inputHash!==inputHash)return json(res,409,{error:'REQUEST_ID_CONTENT_MISMATCH'},true);return json(res,200,old.result,true);}
|
const cacheKey=client.id+':'+input.request_id,inputHash=sha(raw),old=completed.get(cacheKey);if(old){if(old.inputHash!==inputHash)return json(res,409,{error:'REQUEST_ID_CONTENT_MISMATCH'},true);return json(res,200,old.result,true);}
|
||||||
inflight.set(client.id,(inflight.get(client.id)||0)+1);
|
inflight.set(client.id,(inflight.get(client.id)||0)+1);
|
||||||
try{const handler=questionHandler||(await import(pathToFileURL(path.join(__dirname,'question.mjs')).href)).askQuestion;const result=await handler(route,input,cfg.fuel);completed.set(cacheKey,{inputHash,result});if(completed.size>1000)completed.delete(completed.keys().next().value);return json(res,200,result,true);}finally{inflight.set(client.id,(inflight.get(client.id)||1)-1);}
|
try{const handler=questionHandler||(cfg.use_privileged_readonly_worker?async(_route,body)=>askThroughWorker(body):(await import(pathToFileURL(path.join(__dirname,'question.mjs')).href)).askQuestion);const result=await handler(route,input,cfg.fuel);completed.set(cacheKey,{inputHash,result});if(completed.size>1000)completed.delete(completed.keys().next().value);return json(res,200,result,true);}finally{inflight.set(client.id,(inflight.get(client.id)||1)-1);}
|
||||||
}catch(e){if(!res.headersSent)json(res,503,{error:/^[A-Z_]+$/.test(e.message)?e.message:'QUESTION_SERVICE_UNAVAILABLE',reply_withheld:true,no_execution_authority:true},true);else res.destroy();}});
|
}catch(e){if(!res.headersSent)json(res,503,{error:/^[A-Z_]+$/.test(e.message)?e.message:'QUESTION_SERVICE_UNAVAILABLE',reply_withheld:true,no_execution_authority:true},true);else res.destroy();}});
|
||||||
server.requestTimeout=60000;server.headersTimeout=10000;return server;
|
server.requestTimeout=60000;server.headersTimeout=10000;return server;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
runtime/worker.mjs
Normal file
16
runtime/worker.mjs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
// WORLD_RUNTIME: bounded root-owned read-only persona worker; no command, path, neural or memory setter.
|
||||||
|
import fs from 'node:fs';import path from 'node:path';import {askQuestion} from './question.mjs';
|
||||||
|
const ROOT='/etc/guanghu/public-question-worker',CONFIG=ROOT+'/config.json',STATE='/var/lib/guanghu/public-question-worker';
|
||||||
|
const need=(x,c)=>{if(!x)throw Error(c)};let locked=false,lock=STATE+'/request.lock';
|
||||||
|
try{
|
||||||
|
need(process.getuid()===0&&process.argv.length===2,'PRIVATE_WORKER_IDENTITY_REQUIRED');const stat=fs.lstatSync(CONFIG);need(stat.isFile()&&!stat.isSymbolicLink()&&stat.uid===0&&(stat.mode&0o077)===0&&fs.realpathSync(ROOT)===ROOT,'WORKER_CONFIG_NOT_PROTECTED');const cfg=JSON.parse(fs.readFileSync(CONFIG));
|
||||||
|
let raw='';for await(const c of process.stdin){raw+=c;need(Buffer.byteLength(raw)<=16000,'QUESTION_BODY_TOO_LARGE');}let i;try{i=JSON.parse(raw);}catch{throw Error('QUESTION_JSON_REQUIRED');}
|
||||||
|
need(i&&!Array.isArray(i)&&Object.keys(i).every(k=>['persona_id','question','tool_host','session_id','request_id'].includes(k))&&typeof i.question==='string'&&i.question.trim()&&Buffer.byteLength(i.question)<=12000&&['tool_host','session_id','request_id'].every(k=>typeof i[k]==='string'&&/^[-A-Za-z0-9._:]{1,160}$/.test(i[k])),'QUESTION_COORDINATES_INVALID');const route=cfg.personas.find(x=>x.persona_id===i.persona_id);need(route&&cfg.allowed_personas.includes(i.persona_id),'PERSONA_OUTSIDE_WORKER_SCOPE');
|
||||||
|
fs.mkdirSync(STATE,{recursive:true,mode:0o700});need(fs.lstatSync(STATE).uid===0&&!fs.lstatSync(STATE).isSymbolicLink(),'WORKER_STATE_NOT_PROTECTED');
|
||||||
|
if(fs.existsSync(lock)){let pid;try{pid=Number(fs.readFileSync(lock,'utf8'));}catch{}let alive=true;try{if(!pid)throw Error();process.kill(pid,0);}catch{alive=false;}if(!alive)fs.unlinkSync(lock);}
|
||||||
|
try{const fd=fs.openSync(lock,'wx',0o600);fs.writeSync(fd,String(process.pid));fs.closeSync(fd);locked=true;}catch{throw Error('QUESTION_CAPACITY_LIMIT');}
|
||||||
|
const file=STATE+'/rate.json',now=Date.now();let rate=fs.existsSync(file)?JSON.parse(fs.readFileSync(file)):{start:now,count:0};if(now-rate.start>=60000)rate={start:now,count:0};need(rate.count<(cfg.requests_per_minute||12),'QUESTION_CAPACITY_LIMIT');rate.count++;const next=file+'.next';fs.writeFileSync(next,JSON.stringify(rate),{mode:0o600});fs.renameSync(next,file);
|
||||||
|
const result=await askQuestion(route,i,cfg.fuel);console.log(JSON.stringify({...result,guard_scope:'IDENTITY_SOURCE_COORDINATES_EVIDENCE_AND_RACE_NOT_SEMANTIC_CERTAINTY'}));
|
||||||
|
}catch(e){console.log(JSON.stringify({error:/^[A-Z_]+$/.test(e.message)?e.message:'PERSONA_WORKER_SOURCE_UNAVAILABLE',reply_withheld:true,no_neural_state_write:true,no_memory_write:true,no_execution_authority:true}));process.exitCode=2;}
|
||||||
|
finally{if(locked&&fs.existsSync(lock)&&fs.readFileSync(lock,'utf8')===String(process.pid))fs.unlinkSync(lock);}
|
||||||
|
|
@ -6,7 +6,7 @@ for f in sorted(p.rglob('*')):
|
||||||
rel=f.relative_to(p);allowed={'AGENTS.md','entry-data.json','package.json','protocols','runtime','scripts','skills'}
|
rel=f.relative_to(p);allowed={'AGENTS.md','entry-data.json','package.json','protocols','runtime','scripts','skills'}
|
||||||
if rel.parts[0] not in allowed or any(x in rel.parts for x in ['.env','secrets','brain-state.json','transition-log.jsonl']) or f.is_symlink():raise SystemExit('PUBLIC_PACKAGE_PATH_DENIED:'+str(rel))
|
if rel.parts[0] not in allowed or any(x in rel.parts for x in ['.env','secrets','brain-state.json','transition-log.jsonl']) or f.is_symlink():raise SystemExit('PUBLIC_PACKAGE_PATH_DENIED:'+str(rel))
|
||||||
files.append({'path':f.relative_to(p).as_posix(),'sha256':sha(f.read_bytes()),'bytes':f.stat().st_size})
|
files.append({'path':f.relative_to(p).as_posix(),'sha256':sha(f.read_bytes()),'bytes':f.stat().st_size})
|
||||||
man={'schema':'guanghu.public-rule-file-manifest/v1','world_id':'GLW-2.0-0001','version':'20261019.1','scope':'PUBLIC_ENTRY_RULES_SKILLS_AND_QUESTION_TRANSPORT_ONLY','files':files};(p/'MANIFEST.json').write_text(json.dumps(man,ensure_ascii=False,indent=2)+'\n')
|
man={'schema':'guanghu.public-rule-file-manifest/v1','world_id':'GLW-2.0-0001','version':json.loads((p/'entry-data.json').read_text())['version'],'scope':'PUBLIC_ENTRY_RULES_SKILLS_AND_QUESTION_TRANSPORT_ONLY','files':files};(p/'MANIFEST.json').write_text(json.dumps(man,ensure_ascii=False,indent=2)+'\n')
|
||||||
with zipfile.ZipFile(p/'release.zip','w',compression=zipfile.ZIP_DEFLATED) as z:
|
with zipfile.ZipFile(p/'release.zip','w',compression=zipfile.ZIP_DEFLATED) as z:
|
||||||
for n in [x['path'] for x in files]+['MANIFEST.json']:
|
for n in [x['path'] for x in files]+['MANIFEST.json']:
|
||||||
info=zipfile.ZipInfo('guanghu-world-entry/'+n,date_time=(2026,10,19,0,0,0));info.external_attr=(stat.S_IFREG|0o644)<<16;info.compress_type=zipfile.ZIP_DEFLATED;z.writestr(info,(p/n).read_bytes())
|
info=zipfile.ZipInfo('guanghu-world-entry/'+n,date_time=(2026,10,19,0,0,0));info.external_attr=(stat.S_IFREG|0o644)<<16;info.compress_type=zipfile.ZIP_DEFLATED;z.writestr(info,(p/n).read_bytes())
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue