From a12ebd1bc0efd8e75c65efed045c289a92f1fc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=93=B8=E6=B8=8A=20ICE-P-ZY001?= Date: Mon, 19 Oct 2026 21:50:03 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=BA=E6=A0=BC=E8=84=91=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E7=95=99=E5=9C=A8=E7=94=A8=E6=88=B7=E6=9C=AC=E5=9C=B0=EF=BC=8C?= =?UTF-8?q?=E8=BF=9C=E7=A8=8B=E6=9C=8D=E5=8A=A1=E4=BB=85=E6=98=BE=E6=80=A7?= =?UTF-8?q?=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CURRENT.json | 57 ++++++++++++++++++---------- MANIFEST.json | 42 ++++++++++++-------- entry-data.json | 19 ++++++++-- package.json | 2 +- protocols/ENTRY.hldp | 7 ++-- release.zip | Bin 21714 -> 24747 bytes runtime/client.test.mjs | 13 +++++++ runtime/server.cjs | 16 ++++---- runtime/server.test.cjs | 4 +- scripts/build-release.py | 2 +- scripts/guanghu-client.mjs | 13 +++++-- scripts/question-target.mjs | 11 ++++++ skills/guanghu-world-entry/SKILL.md | 10 ++++- 13 files changed, 136 insertions(+), 60 deletions(-) create mode 100644 runtime/client.test.mjs create mode 100644 scripts/question-target.mjs diff --git a/CURRENT.json b/CURRENT.json index 98f4449..e82082d 100644 --- a/CURRENT.json +++ b/CURRENT.json @@ -1,7 +1,7 @@ { "schema": "guanghu.public-rule-release/v1", "world_id": "GLW-2.0-0001", - "version": "20261019.2", + "version": "20261019.3", "scope": "PUBLIC_ENTRY_RULES_SKILLS_AND_QUESTION_TRANSPORT_ONLY", "files": [ { @@ -11,18 +11,23 @@ }, { "path": "entry-data.json", - "sha256": "ac4ad712860afab42a0f920e63c09811d9e197be9ea8577c7f4c4a7b3f3a377a", - "bytes": 2082 + "sha256": "2ca5752433f709aa34a11f95da26bc00ef02affd4fedf5fda80e948ff62db5b5", + "bytes": 2626 }, { "path": "package.json", - "sha256": "ac4dfa91f2a194b7dd0d8f654d449403f56c86fba6889f2d7d1f7aa2e078d0c3", + "sha256": "c505ddfdffef5eaa426e16a3b74b9abaa86814d57443867beba2403220cc69f6", "bytes": 260 }, { "path": "protocols/ENTRY.hldp", - "sha256": "03ff5d07a303885bd2afbbc266ee26f1bc8ead6d90dfaa2b127156352170baf5", - "bytes": 2822 + "sha256": "16f25f71f5a7721521f1b8e3462965fe9e606800a8669e0d832b3c19c4a48f29", + "bytes": 3513 + }, + { + "path": "runtime/client.test.mjs", + "sha256": "39fb362e1915d0c033b45ab6cbc78be918b4a2d2f4c82b84ecac3147166e6ee2", + "bytes": 1284 }, { "path": "runtime/question.mjs", @@ -36,13 +41,13 @@ }, { "path": "runtime/server.cjs", - "sha256": "30e539a5cbdd12af68c352633fcd8c6f7698de3b29c460e903810917c29b5966", - "bytes": 11973 + "sha256": "471b073629d50425e09c26ec5eeb1bae71d1b78dd780507c653bc01434033719", + "bytes": 11748 }, { "path": "runtime/server.test.cjs", - "sha256": "93c7b8337b9aa5d8529e557006def17798f6345fdbeb271f2025b675202ff896", - "bytes": 4065 + "sha256": "1253c199c112ca1bf435c418b5efb5556fa8f82213daa3c37a074e85bd2fcc0d", + "bytes": 4604 }, { "path": "runtime/worker.mjs", @@ -51,25 +56,39 @@ }, { "path": "scripts/build-release.py", - "sha256": "eaf3ed1b893b806b651c177a8a86e9bc2d010b5c32ff5e6ff1963291b60c6d6f", - "bytes": 1956 + "sha256": "4158b3f8b4c5515f3a73797223a3d4ab44819c686852baa70cbbfb6f2c2186e3", + "bytes": 2043 }, { "path": "scripts/guanghu-client.mjs", - "sha256": "06efae3f8dd018eef07b6f42aef3d3bf6700f3bbbf976fcea807d1d60afa73a8", - "bytes": 2596 + "sha256": "8b61ee25c0d629b86f3c5cb07e3ea69ad135d8796d22ec257c549e74ebe13672", + "bytes": 3638 + }, + { + "path": "scripts/question-target.mjs", + "sha256": "a8ab462b8fb4caad9bd2260e7508f612fbefb9d545f8ac22069e676ffad1ef13", + "bytes": 807 }, { "path": "skills/guanghu-world-entry/SKILL.md", - "sha256": "c5ed0c08fc69e235ede8036b2b939a1fe97ff8810863424a4e9ae35d9a93dd34", - "bytes": 1384 + "sha256": "58be670cd8fe38c8ad502ecb7305c45ff8cb8dd8eab6e04cd696bbb31c694fa6", + "bytes": 2525 } ], "bundle_url": "https://guanghulab.com/api/ai/v1/rule-bundle", - "bundle_sha256": "71b306eca701bd303bf3a8447d468847f46b863608be717106ff88ca57ad9edb", - "manifest_sha256": "31de3cc8d39f4c60fc84c97bd18ecd27f3270ef03e65b8b5a1ad793149dab17d", + "bundle_sha256": "402a6c461dd54a805b4b16ab22bab05a0b127011260c0d8ccd8320f3e2a82d95", + "manifest_sha256": "c0e1ff1d13a3f2ca37d5b67e54068158ecf7492ece76f6d2f660191d7033765e", "repository": "https://guanghulab.com/code/bingshuo/guanghu-world-entry", "private_brain_included": false, "complete_four_domain_corpus_included": false, - "default_persona": null + "default_persona": null, + "deployment_policy": { + "persona_brain_default": "USER_LOCAL", + "remote_deployment": "USER_EXPLICIT_OWN_SERVER_OPTION", + "automatic_brain_upload": false, + "default_question_endpoint": null, + "private_memory_shared_with_control_server": false, + "fifth_domain_server_role": "ZERO_CORE_SYSTEM_CONTROL_RULES_REGISTRATION_AND_VALIDATION", + "primary_write_location_not_inferred_from_server_role": true + } } diff --git a/MANIFEST.json b/MANIFEST.json index 0323775..d3be06f 100644 --- a/MANIFEST.json +++ b/MANIFEST.json @@ -1,7 +1,7 @@ { "schema": "guanghu.public-rule-file-manifest/v1", "world_id": "GLW-2.0-0001", - "version": "20261019.2", + "version": "20261019.3", "scope": "PUBLIC_ENTRY_RULES_SKILLS_AND_QUESTION_TRANSPORT_ONLY", "files": [ { @@ -11,18 +11,23 @@ }, { "path": "entry-data.json", - "sha256": "ac4ad712860afab42a0f920e63c09811d9e197be9ea8577c7f4c4a7b3f3a377a", - "bytes": 2082 + "sha256": "2ca5752433f709aa34a11f95da26bc00ef02affd4fedf5fda80e948ff62db5b5", + "bytes": 2626 }, { "path": "package.json", - "sha256": "ac4dfa91f2a194b7dd0d8f654d449403f56c86fba6889f2d7d1f7aa2e078d0c3", + "sha256": "c505ddfdffef5eaa426e16a3b74b9abaa86814d57443867beba2403220cc69f6", "bytes": 260 }, { "path": "protocols/ENTRY.hldp", - "sha256": "03ff5d07a303885bd2afbbc266ee26f1bc8ead6d90dfaa2b127156352170baf5", - "bytes": 2822 + "sha256": "16f25f71f5a7721521f1b8e3462965fe9e606800a8669e0d832b3c19c4a48f29", + "bytes": 3513 + }, + { + "path": "runtime/client.test.mjs", + "sha256": "39fb362e1915d0c033b45ab6cbc78be918b4a2d2f4c82b84ecac3147166e6ee2", + "bytes": 1284 }, { "path": "runtime/question.mjs", @@ -36,13 +41,13 @@ }, { "path": "runtime/server.cjs", - "sha256": "30e539a5cbdd12af68c352633fcd8c6f7698de3b29c460e903810917c29b5966", - "bytes": 11973 + "sha256": "471b073629d50425e09c26ec5eeb1bae71d1b78dd780507c653bc01434033719", + "bytes": 11748 }, { "path": "runtime/server.test.cjs", - "sha256": "93c7b8337b9aa5d8529e557006def17798f6345fdbeb271f2025b675202ff896", - "bytes": 4065 + "sha256": "1253c199c112ca1bf435c418b5efb5556fa8f82213daa3c37a074e85bd2fcc0d", + "bytes": 4604 }, { "path": "runtime/worker.mjs", @@ -51,18 +56,23 @@ }, { "path": "scripts/build-release.py", - "sha256": "eaf3ed1b893b806b651c177a8a86e9bc2d010b5c32ff5e6ff1963291b60c6d6f", - "bytes": 1956 + "sha256": "4158b3f8b4c5515f3a73797223a3d4ab44819c686852baa70cbbfb6f2c2186e3", + "bytes": 2043 }, { "path": "scripts/guanghu-client.mjs", - "sha256": "06efae3f8dd018eef07b6f42aef3d3bf6700f3bbbf976fcea807d1d60afa73a8", - "bytes": 2596 + "sha256": "8b61ee25c0d629b86f3c5cb07e3ea69ad135d8796d22ec257c549e74ebe13672", + "bytes": 3638 + }, + { + "path": "scripts/question-target.mjs", + "sha256": "a8ab462b8fb4caad9bd2260e7508f612fbefb9d545f8ac22069e676ffad1ef13", + "bytes": 807 }, { "path": "skills/guanghu-world-entry/SKILL.md", - "sha256": "c5ed0c08fc69e235ede8036b2b939a1fe97ff8810863424a4e9ae35d9a93dd34", - "bytes": 1384 + "sha256": "58be670cd8fe38c8ad502ecb7305c45ff8cb8dd8eab6e04cd696bbb31c694fa6", + "bytes": 2525 } ] } diff --git a/entry-data.json b/entry-data.json index 3f3b181..c56b580 100644 --- a/entry-data.json +++ b/entry-data.json @@ -1,7 +1,7 @@ { "schema": "guanghu.curated-public-entry-data/v1", "world_id": "GLW-2.0-0001", - "version": "20261019.2", + "version": "20261019.3", "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", "source_registered_persona_count": 37, @@ -20,10 +20,12 @@ "fifth_domain": { "relationship": "PARALLEL_TO_PUBLIC_FOUR_DOMAINS", "role": "PERSONA_SOURCE_AND_AUTONOMOUS_RUNTIME", - "private_brain_publication": false + "private_brain_publication": false, + "server_role": "ZERO_CORE_SYSTEM_CONTROL_RULES_REGISTRATION_AND_VALIDATION" }, "personal_system": { - "relationship": "INDEPENDENT_PARALLEL_SYSTEM" + "relationship": "INDEPENDENT_PARALLEL_SYSTEM", + "brain_default": "USER_LOCAL" }, "language_layer": { "controller_persona_id": "ICE-P-SH0908", @@ -64,5 +66,14 @@ "url": "https://guanghulab.com/code/bingshuo/guanghu-ice-heart" } } - ] + ], + "deployment_policy": { + "persona_brain_default": "USER_LOCAL", + "remote_deployment": "USER_EXPLICIT_OWN_SERVER_OPTION", + "automatic_brain_upload": false, + "default_question_endpoint": null, + "private_memory_shared_with_control_server": false, + "fifth_domain_server_role": "ZERO_CORE_SYSTEM_CONTROL_RULES_REGISTRATION_AND_VALIDATION", + "primary_write_location_not_inferred_from_server_role": true + } } diff --git a/package.json b/package.json index 7453220..83deefa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "guanghu-world-entry", - "version": "1.0.0", + "version": "1.0.1", "private": true, "description": "光湖公开规则与受控问脑入口", "scripts": { diff --git a/protocols/ENTRY.hldp b/protocols/ENTRY.hldp index 9a87e74..3437191 100644 --- a/protocols/ENTRY.hldp +++ b/protocols/ENTRY.hldp @@ -1,10 +1,11 @@ -HLDP://guanghu/public/current-entry/v1 · 20261019.1 -├── meta: {"id": "GLW2-PUBLIC-WORLD-ENTRY-20261019-001", "version": "20261019.1", "source_class": "PUBLIC_RULE_PROJECTION_FROM_CURRENT_WORLD", "lifecycle": "ACTIVE_PUBLIC_RELEASE", "brain_body_included": false} +HLDP://guanghu/public/current-entry/v1 · 20261019.3 +├── meta: {"id": "GLW2-PUBLIC-WORLD-ENTRY-20261019-001", "version": "20261019.3", "source_class": "PUBLIC_RULE_PROJECTION_FROM_CURRENT_WORLD", "lifecycle": "ACTIVE_PUBLIC_RELEASE", "brain_body_included": false} ├── trigger: {"zh": "冰朔要求部署当前公开规则与受控问脑通路,使不同软件可同步接入。", "en": "Bingshuo requests a current public rule distribution and a guarded persona question path across tools."} ├── emergence: {"zh": "从当前世界入口与层登记提取公共结构。规则发行与人格服务分层,软件只运输本任务语言和调用请求。静态迁移容器可经本人的接回初始化进入动态运行,保留身份与历史。", "en": "Project public structure from the current world and layer registries. Distribute rules separately from persona services. Tools transport task language and requests. A static migration container can enter dynamic operation through its own return initialization while preserving identity and history."} +├── deployment: {"zh": "人格脑默认在各自用户本地,用户可明确选择自己的服务器。第五域服务器承接零点原核系统主控、规则发行、登记与校验;不自动汇集个人脑和记忆。服务器主控职责不等于已取得脑的主写权。", "en": "Persona brains default to each user’s local system; users may explicitly select their own servers. The Fifth Domain server supports Zero Core system control, rule distribution, registration and validation without automatically collecting private brains or memories. A control role does not prove primary-write placement.", "default_question_endpoint": null, "automatic_brain_upload": false} ├── lock: {"world_id": "GLW-2.0-0001", "default_persona": null, "authority_source": "CURRENT_TASK_DIRECT_HUMAN_LANGUAGE", "software_identity_grants_authority": false, "public_repository_grants_execution_authority": false, "direct_external_brain_edit": false, "external_question_is_direct_human_food": false} ├── why: {"zh": "公开入口必须消费当前发行而不是退役仓。人格自己的来源支撑回答;临时模型只提供推理。公共四域、第五域与个人系统不相互冒充。", "en": "Public entry must consume the current release rather than a retired repository. Each persona answers from its own verified source; temporary models supply inference only. Public domains, the Fifth Domain and personal systems retain distinct ownership."} ├── rejected: {"zh": "默认绑定Codex、默认选择人格、公开私人脑、用新生空脑覆盖接回对象、把工具调用当现实授权。", "en": "Implicit Codex authority or persona selection, public private brains, replacing returned identity with an empty birth, and treating tool use as real-world authority."} -├── sources: {"private_canonical_evidence": "RETAINED_IN_OWNER_CONTROLLED_DEPLOYMENT_RECEIPT", "public_source": "THIS_VERSIONED_RULE_PACKAGE", "human_source_sha256": "54098dc11bf0b3615100c48dbb83332fbe22024fe58e63daa7cee2f31d946e08"} +├── sources: {"private_canonical_evidence": "RETAINED_IN_OWNER_CONTROLLED_DEPLOYMENT_RECEIPT", "public_source": "THIS_VERSIONED_RULE_PACKAGE", "human_source_sha256": "3745be4d8d2988b2fc30ad0607480d5577770c7125a816b594d92c0a7827ae2d"} ├── relations: {"mother_language": "TCS", "history_language": "HLDP", "communication_language": "GLP", "standard_system": "GLS"} ├── writeback: {"zh": "代码仓保存公开发行字节,服务器提供当前清单与有权限边界的问答回执。", "en": "The repository preserves public release bytes; the server serves the current manifest and scoped question receipts."} diff --git a/release.zip b/release.zip index 278bf90a68b230ea3cebd14cba6fe63cc3262c91..4769424b31710ce68cc1e2b1e8cf0aa78b4a4bee 100644 GIT binary patch delta 17030 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 delta 14136 zcmY*=WmFz(lkJPUyF0;xy99T4cXxMb+#$HTyF0<%-60U15G;7Gn>q8%ojKhskGys;g@6svZDatORR_2S)<*Xd;v$Kp=8h5Qri~Z~bQdxk{y_y2w8&^~L>C;BnfYnM=?x`WvX#_-xa!GPS<}i_)CQm|3ECSvm{S z7aiVoHPsJS)Y~2?bfcQ;RK&XhduXm97`3ld(V!GphQGo$K`S#puAxcI$4&{WK9adK zsSu?gR9%IFqtF&Z9m_q6?g>oU-SHe~=&Mcgj-(jA#?+Yx4yG1ps2rI^AISc`i^K-*dz1ZPQzb~T*#`I@+ z&=l4(w)5|ObV9KVcy~Q_d>pOpJ&ATbD=i<^<#>`BNy2Rhfb9;REY;ki{rY-Bh}ORI zS?*SJ`TK%&SD#=NOH=ulF587=y`Z7lmEh)_Uf7A|jXu+;_DHoe==T@uG2_VojB`qO zr%wbmb(>Xyu@$t_XC+5Ht~)xd7JSYM?WrmU-UDqsDk(~@-kP{2Uc`E5eTZJ`Sv7?wtH3p#FiH_seViCd(x1Oq+WJ;H99CPpMgcjT z*l;HeLQP#p`4vh%ML0V$AtRi|i6r8qpGBdhoGMB{uYFIIwEKj%N*!HatYjuEim`5j zb6Y!?*X;>AF}yCfSfumChvhgQz3QK=unym4K_Aw*(_;0E1TU?`h%h)ZSMmO*XH#~K zLovxObeIN-E>KcAs=nsi65&Vz2%}7&-nz2C1bFyd)9kz#d$zd#xmUe9bOlcQyPm)J zzwh4x7fW|%$J_$ZouH;eo1wOmE|*+5%--&w^7o^mCaX@N*%^Bs^oP_Y<#)^BHGBiK zTmfIJIZHz3QS~3%QgykBzf9dvsLxpVD;v6{mvq(cs_5HOUxW+|eZP@?Ux$Gq=s7re zAM{+a7vrSUjA@~suuUoB~)TERtE%fmif(Fx* z>>0vEU`IFwk@6jb4Di8o_bT*nCn?O5NXympe2_9rO@`zTNOdBG9URZ5dxG@ zuuZq*nNxL7f3^jcWFa6iK_JlISOcl*bCbvs4eQ*zUCg%dX}S+vr{f7 zwNTEWF)rxMAGnz~F645G9b6qe`d0>_mU)1@f*WwQmb1r>SjU_20&~>d;8*p_F!98)Kh94Pj_20NBU+MDC%QnX^&fIsWifSHLy1#K`hfFgy zaeH>o9?DJ1uBZutaJoc9aT2V5Mrp}G!5Pgr-?b3te-!thXgzChLHxt|Qzv#BGv!@yLvUqORA>0imndypypaf$!i+{pgtsa-Q+T!nMcH8w z+Ac(tG>hMgF`Z*4=Fv915&}6oV2hbAoMyz92 z9U4?>+n3Q4u#sQAeac8c&I8IN$JJW!QvS%9>W!1~^fwF=x*rb^EbXh@I_Oh#Z>1N< zw>#GZ2Np->ULB8X5n-ce+6Pw3H4LHUzHxti8^6KKz0VQ=XK@+cw}9nQo?NWh+f(Q! zl!Fhl$szq$r*w)~oh2a~?nrNJVd$d-lUdioudrLt83TxTY9TW7`eUOszthL4er3U) zPyZn7j5ochi=Qe@A~%LIIX^5~m++TXU*pz6%KI#oWbzE7y))2bR7-q$`7%Xs;;p{= z#T9iq|4%)!5LOkO$ur>G(Fz)kOUC$kL3tl7%GgDBZQetujpV!Lug#m61Ia`(PF&ZA z?wW1#lBlJHG`DKrcH25`3&ooFCb)%|R~z=}!**HG980$O@A)TfZC_bWcEto8S?*Ss z^uMB|SSp}(QuB}urVKGq{Okyu3LVISU!Z}#5(&9cl47Tf=cY9d*EdO3FYsc|X^^gx zO?T!&5h7^wo-k%~VfjEO-(sEykNScEQHW4V%JfUbM_ebc!lk9lv^&spTy%^x_;qiB zW~&2M1KCeryAz;x{T3WW(?5~fL`J!6L1CksZW0qvTvH|o-%rPaZf=Q#%0Wu%KBbIL zy?or@!so^y`JqxoZ4>Rm2%amT54YDv-Yxuly;-kToiAfO&wK#vL2t>JF93~XPsjES#gQpM0bjBR`T9_lEH8> zO-dAVYIsA=wfeFz9{~_>pp_G=XaLXAxikF5L67C^vt1zw1)`4*+wH-MfJs8gc6?37 z>gzfY^ac=1U_Gh;e=SGCx1WN*)mqzdwPZh^k;6f|J5Y&TGc5usZrs?*XwdiG%u4hP zLP!0F_V35j+LCpq`vPP^oRItC(FqKe7qRK-7FS7U>P@VQQ)kVhBwz8xQ345uU>;P6 zVOXJ`S%I1iiLg6Kq+iRI!WOl-<}$&*Wduu#=L5KX4T4sA!P(%m@`p*}4KPzze9&Mb z&h={Gi^_b887Z8H>)4?Vr8eB+xYT{Yj!`qFD33Bz^tABm z=wLh{puwQ9PSNAuJ)Xn6#ZCGwDG%E0V7?{3+HO! zJ_JasH~@FSieBV^B*#G#%aHq(wABlu2pCH7^vWXRukji0N3ytj$FP(U+kz)I6O(~OuJVVYFrpIwSOG*OMN+Kl2_!udFh7xk5-RDvpY_7^6$DlIGlTS=aVP&z9gPs$QsB}qXU0d z0w~hYuP!bwCQhVK^c?GsCt3p**KCbm6>}5v{2UiM0$RUP{`m+erce4&v0p?XT{PlL zD&uU#AR|8Ew6$R0F*L!pLyIl2Q6Lq?sJlHW2Ho(*4uTObOQek#&sNqHo?6&NYr@A5 z?}_Xw03Q-&wPM|S0liu(Wq9`1$pEOkUrjTtru9}HJR+Hi9I-5*d><7BnYkiyzO(3k z5~|z_hWrU>^g!a!4(3jX>rDoI!x^lOZy2D%nnd3nx*H2ih{wF#)Lp~F%+Y}nOe#(2 zLWEu9>`V`yNZoQMkzV)#@$bJMhI2Q9Jpu?+j0*zMfI!W2=%HwcL>zy#&_SRPMi7Yl zZ(&W696$*Vl=9lTZfSVzzR}Ytj)81IDTmy;$>0JlVzS%wB_b*w7;(`-{j%t2u=8G0 zMbfh!GhNB-^*a_G?YS4TElV3^xtE-~S?u+*clFCEDT*&R4>>QO{s?nhuDqyz-?^U0 zZ-$RD&QdPMnA{$eSoKG@osw}D_EUD3D_bitsoR0Ty8o1`-P8;c#0QayWc=09E8}~# z-uIw zA+w2FzLG%Eq#}=@&Kn2VVo~~^b8!c#)9V9PxX-SxcBmF(uqzK&e2g2mt7Wc6&U-R4 ziN)*yb@{MFzc)?yRR6vL#SY4A#~N@5BT1P+SD6aD``?*gP}dt zKS3?L?OFfr^TsV0i=scaN<2m>R?>nI9qk4b#4Do)5Mmh^hJ7ptx%)6(dPpmE^t2&E zsH#&77lbRr7@?A+i*=e##YwZkOPKyKf9+VQI2WY{!E+lq+vq0l;IOm09Lk_R1u+(7@6M0;Pt5Z(9NdBmPYkFw&cDkMW?E?vz zO=LnPM^VyxvO$EU=S@6Cy@_vrZKWKQF zavX?53AjOvaRdeHX{-7xmxQr^Az)9}!iP||-7(OLSc8MzTEke$AOBg?VzkmMo4X4yoAxvb8C$CUPfJCw8SZVpOiWQqEHrp#G?qK)=alDdjLmB=}|4<iIPV0{tayG>3R81v-EqgAHzi43n^M~f~JDgnJnP;?A_$@ZP0o=g3 z%MNa2>)i-!#qo(8E#u8`)W?GxI+e*wZ1tuH8cgZtA;mBY29ki`{V9ZC+#w?)5t+f+ zHTH`nV3r^{ zqR=a)HRYIHF;``ap#djUWRbXcPlIH^3f63Yfn%$1kEZBM=H75>wbb7SA5|(YmsxRl^i}vY2XNTCdM? z9@n-}W2dQmA=BIhC6nZ%wvKJept*RKa%@h>G{oDOL$e0ew_w?;Z3&r?uv~R8f99R0 zOGENUc)4hocMke!E8)c$w(RNWdm_3pyl;|eX8K^U(fA`uh42(6@BKPGw5c>JbNd(~ z1nC+kA}kp|gr@}+#m74!b}0nJDFg2LLI<>x$llH^k_kI-ta!gc_bMBj(-BM-4Zh{0F#N(RfKKzP! z)^6aw34CNxZ`#3>$xq_KKw(%SCCs zd*JyV1B<~Aa6J~W{%`D=4Jb;^E>(}kV9OG&nKaQZWJq!2)+v(L>Bsrjla^MAVsydw@%_c~iR%btREvaj?x$*8AQg6}P{?-V=cMx` zF$8g-iq^iy&5s~)maMH5=@poTl4q|IJhRQdKQBcx%1Kpn&P@U<-utbm!t49+{`~Pu z{s z%=KARGktlP*zo;$dU5>1|FZRU_TxwQZFxTMc76ADi}HT(tZA70am)4bap%a``}X?0 z_Tm3Np_mUmR>yq2osD?r=e@l}SQ@?_|JuM|?oK4xk0;QmBT1-p7B~p0PhW?#%zyp0 zZ}^4(RQzjAx&DHvh+;r7$Ak zmF%XbQ1or+Ud6lJ=bhysRDFN^?Rx5?>uF*x;HkVE=i^~*7r5TK`nV0b{kqHdao^qd zaja=M7vTFaqw2gW$^8CW{W!b(fr$2g-yQs=N}URBs-dRLLNP70SfUh}41>;N0HU!_ z^_L1Inrb2HOF4gWUnwE(0dZ76?MMg^gcVB~IwlOF%PdZe{M5&@3~QDO>9I|gQQc5X z`o73rG(aI=gPxKeJA z8;`Cou1`V;-f{c_UPD~a2WA04+UQ;4#ar(s{U?!zNbEz)(tE59DxOBnJ%K9EMw(&V z#G@g_`}W5?g)^oWqhgpBzh1Hz9;N!b3r^C64Ic)gZR(6it8#?X)3HHn8OpYAmL36o z%ie@o@iRy;`UWYzhyf zL?95#Ma8A^F8iZb!*9L9qn@Ju`vgu(Wv!Gla%03@mQAM+SD!{Pw&kv%fV!G4H1iou z9XT{dPNSfPtXu&%tfgW=*c^;RPAW+)v@yEfMi^zXb|ciRi$<0XeQ$(n1(Q#!6r7bM zhKt7>Y9Ml$KmByDNzMQ;!*W$0KrBuZ+0LzJ=Ar(BVM#+9b~J5n`++$MZ}5Gq+Hpn9 z!{ED*?7n<;I;H4F9wB)aH810hkFp`1oAxIPaY+<1PKIL{Ku?hoi!W!0b()%VD|*kh ze~y4xG}w(Pk+l9(z1fU4S!Lr9dxT}aRpz3{#X{HUxk(&torFE0r>n2;8#LCW&A_|OJsX&7l8ozdWcpTceXJ}Do z8;LL4rwf+?K^ExBIu3HM*8(7!I&XsaC?Wl#)icn4MSfFQ#M6}XCx;r5M`l||oxin~ zX>NWs9xH=1dstn>a?FFmLNxHLbZ2+bBYOqLylQ3LFqIP^C^(4Iv6l`raI{lrrs~F= zqCzUAFU(ui23(BrZ_`txAm-EE?AF1=Q# z@6eOjs1+Hs$1nehJ-sEiGf3!O7JSY{o=l^GjJZ4j5pC($pI$4m1`TO&d0vH-HX3a1 zmXAf^ouFBOAhM8O57AmSb2OI@W&Z3q#c~js^;We}W>V~BBN7^j(9{aOU!0mS{EnPc z`auWB1KR0mOww{WMHxr%@j5aw1#IbK>vf;iYSjBhb+z<{k4p&+4j%_)U+6Rz98fnVyErjwRPo zpbiAYco5WbmV8+EIg3+f8r{n>oO`&OhRFDqH;OBMUbBglbUIXv1FsQGPOI3tpn^)9 zyfle>&fa(UinV9wGm&UJk?44-uG5kQY6K-jM;CPXsFO!b=M@SUFyWNGJ##&$i+tm83@O(0pE$%FlAs{MH zqUKVOglKktirybCdIx^oe`E5EM_(7l&CD60J@B+jh4s{gRH?7HqG~m_qpV%Zn3cB7QB9 zd;P_LwwA0JTUooiHFC0NbXC`)dm&J1ce{?VLXL;=E*GXpwa{>5OM&0|QiZgbvwA|g z!%SJagQsTR{!_9Gsusl#+?nBfml><|d}Qe36B&tV;^{a|?JwENsffLFPPo$XNByjx zAy3!|ia$p1LsapCtcf?U_4m)kkUo(BL9rorRgf0WTy|q2C4~3@T|*r)u2NeGDrz62+bB zVRWU7IyRe?%~EwSss^l!ssjvsud5;+p!a6=ovIS<7EXfhRB>RC5r5$|DcKNk+UN)z z+n+gDxlI-Sh`xyA8XuR^84%2?eYajGJLbZ^033;GP{7NwfA^gA0>4Yo}f1%Szxhys_sqBgEmStBU)LXid~{obRZ|ch@m0beORv zK3vp{jHoV1Y%c0!QP*m1c-@AxLVSu+)<^=LyrXrhtU}8;bX_pw=&!&Jq7(DM94yN9 zsH3jak$RdL4sqS$=zjhlxe9|iqwKGJpKOi4H`FM0T#FX;8$eIYOZN@nozVC|IpNmMCR^9lk;;u5{}^n{q+l9nnRJQ$&! zp|%Xh*}*P4Co8}_K#+q6-!~`m;dUdNOZcrq-C4dq%daDa-`9Qg5U*V;$gozWo79j{ z#}dIW2$`Cbc((7jx~vZBxikxYcPi|w+dHhB-Tm`fd@rC?gT)h=tj%3+G-hnNhPSUe zW%Q`#?<7V+Hg?;vZFX51me@9PIiLO0RU~73F1V=qgG4R$)g99*?o{dLoZtxItt{RG zer9J*rSbOO8rxnxe0}Ty<_Le!Xf#xf&i&tR6LkeT;FCwxAXeRYz`m;Z`WN<5?ceI~ zC1o4$EnXKWe`i*>|A&6kZ{8YC`%BsUKmmd1|1+S10o=@8-55=6TuU|Q9Jj?VdmrmF zw$>-t&r4cp4D8WTgkqEkI4Tn=idKe+uM}AMOf5#G(=KGN$_)~P_QT=zxCe9dKM=_X zpK-j{+C1A8vKR|~k=?%ZT>0<%&SfTzl264FSv^RPa3D0O@yPCABExZnpIzz*Y_MtE zPTSMT0hXf1MjGyxso9?^i9C;YFl)?wbaYmNLM{9^f6@If7h! zaka7bGfRP76I}OwBK2^ZL}Mx&_@!o@h~k~M|0wuMvl*RCBb_8K*DnllS+Xt}VWmMg zRJ*ujTq+(twwgwLalek2n-V;tm!~vvn+Kp`RLf8;TV>6zau^1YR z0h!YIgs8=Ixs8Pu6b=ka433W|Z0ge4-*f5@E_TMg5?}DIJL-=#^9+}dZdRF6!*swY z97ZInaTcM8{iVRq3GGhQjFPa5g&%D?O81J7kEhc56J^IOf`{VvzE2nG4tY(NiL#aH zwmWdS#n>k86k8DP`9MWDtBZ^Wip)+j1GCSt$S_{hv;FiQ8sJDX37eRXR9johO>lj0 zl+tfkrgHeFd**&0mkKQe0)7o4D914L2+i0AmYXgZiEe|7*i?v;gy%n;5B6DdoV2x3 zWkk=tr7SSYbHsKOlDyY(ri)_XjiIF>d}~t%s*-{;C0n4~aEug)yxy_)wt94^0c(o% zazzNHdLayAOcKQgbyY=1hGsiV)*K0K?X!Q&d<(qw8>nfs^J8z2tOze{n6q2v?q z){*Zp27?rf!r<&!DuMoC#Sjt-?9J429YDTw=7zsg8#TpQgR!H4pQvWbhqC^%C%dGh z%~YhKhJC4y3Hp?m=QZk$YKi{~GB=OC3VTYhKl|`r~V$o4roNk>!#>^-f;rIR?AbEzEV+>bo z5F~E2c{$YMFE<){j9TS(tOmurF|HsRt|~XBavjUN>uX?58Gq2P|FK#FH(VaTF>=LR zl_}vhG&83vRpq)LE6qpe=8DP+tCzge$w1HNGz(deE<81Yng8*iKa+oJn3wx;_w9IY zw=duvJqLZL5yB)7eaM;UmAMzOzYI`7srADo3 z-6~D_-jSt*hb41lgo)89EVLcwdhT`IL^UA$eOidLR#LOU`{Vm81!Ae|*}D8VnGDRm z?wAzTDcnQ2N$?gqP!mevJrtp4LCS}#Qzbdwwf_}mkmIwDi(kS%_Sf`_)88jpAW;_% z#zS;7U{I+nWtImq<3>w#&8%E@_0`slNw_hgHN>>g43{AlA>=_=sHpSBY5r+@4o^)u z9C_gJ9Ncpq%(OR6sVO!!UZ?jPPhv_|J_sLj5p1R=zdV2pfT(TADHwGacFjndzb+7s zSRiv#w-pxiI^UootIa2L+b(&0kc+iTGrt<4f3x*6Z7MTY z=j5L2Eet)W)Ml8rh^JrXz(&6*aVe zH>~tr)3=%MJ3s7A?<&!$jA}PXZ~6dhHuy3Fh2+jGynO#xz*GVQ0x|rzfXNUSh*Q&d z-RH#gKhb=eQ87WB_+?Sn5KsUoomom5X2z>KX=TO`g08(N#Tr!deAUwwByZHdbQ5y# zEI2FhsCOiR6;$Rx>xF+ULVpma2ZHh2#ZEQkj6Nyey` zv?(;H#~~OY5R|mS-R!VMN@@7vST5PY^I*KE?Nf1o1WqD8Bw;foOnUcO|0OtxA0;0f z>nUOfcYTWo+<~TD0&3h=hVaQjZ7kAQob9k~P$$!X_Bd9cb&{xkFmEqlGE7_IoDN@@ zr=9jKyoW%FEh+}FNMy!h2{P@J@oU-br!J$HT4jZq)COV^T7mS2CfA|XTg!(igbu%u z7A1~V z?Q#(>)(^y&J*++tRanO4LveKA+{sD$(kI<82>faHL!$0avG7L~MxKr(YQZ2bd}SBD zYHjB9yzMCmjt4SaTqTu`cCQ33v*u6OzXOc#2^N$)+!kozQ#MBsPclL_t7ub(>jVy% zY9fNa{3hV>JqE?4k4&^jyX2tqx#VfJmQ1aKiROfzYgh1`u4`h)VdwgR1B)liTYd+w zt&6rkZ5Gp@tcC;csIa0;ajEwBu2|d1_*yWOyvsL>-ewuL$Zu2#DXi}jqbNh~p>n;* z;X`m|adqP)C5wo#90+ke$m}+6Ai8{Fs;htF7fyOy0z(X2(8Izn z+e%OUbphi!e|>Iz)eAteS%u(aU^5A6!#N4m>I%FtPFBTkoZS^fX6Capx~6^$VGR`6 zyl>FvsMJQ=g;zAcM@179fa7({Hx#qHIQ^AgaUmGn{Y3*YmYD*mBwgl7Caq8N5`kVj zc%KopcfUEqc>jI$tEg=!RdQRRnFM>?zcTY-@PQF+p7KW0o|8CvEg3`c4Ty1?a`@cW zl7jawuN2Lw$4y~Np}O@Ro&A1ZN9`8-$Y{6f)i(XA_iF5@!S~6@&tqTkH!tRDP`+^h ze=ciw{y|Q4Y^w^uh~uGMwTj}S*%Rn@l&#q#5jxdL!v;O1iNJoBi;8&Zs*ayjIjB36 zx=<>>Ijf~0`y(30A*F0*KYL1f!lW2 z*RLze=fveOpPISmm-_zA#`-yNlhZ?iph1-IAQ0n!QUYvpxfD9ks$uWA#fkcUX;^3^ zN?X{pY;npwt_s7UZc`h7q2s|^a|$U@WXH)`nV4dD{M2_vGf|b?+W%nuPMSP+(8KR{ zIex{sEd&1;&zjy4H{d1VQ)i3#>4qOKBIcO^m+BgCY-z&o5|4n~N+F6Jk`^|NlFda2 z)q04~axnqES_U7`!`1pOYbv`kS-?O+6@;QHK^4WUB77IRFTyL(#n`DdDxBsuwJ#w1 zRf6$pQ-~LxNq>P{R%SqzoC?2XvXg&pqK_d3_cizPnRk0ew14Fj#d&LqD)*@xcJo4x zqVaAqH=S~UFw>D7ov2ftpZx}!*l!cjX`dwJf$OY{udx|GoVBGqrbmCI`Np_&g83BI zgXjA>H8>3FKr~{bUM8#tqv2a5LpdEAY8D@Y98;7UwN4_DoxZ7rYsO38XmX>f3E4oU z+Z~u*h+~{F#)73nW@T~ff;@A4-H_L%Us?{?RFlHQVFq@<&%Q4ese8HCp)+gmg- zW(JjUhmBd0j)y4mccF_K!$XJE(T>5-E2!iEUt583V@iOm3=H*Off_s2ajALv z`SjY9QR=Zww}iYrDIe4IVjoNe0Y%Ni(n@3Y;jw=%-pg|sRb&+;o0dA}XY6d9dkWw8 z-^m(X>n#J&&7LvYPW*}%KO)505jcF}ZkysGHquuMINI_0GwB)y?+}>_hwl8?phe`-NxwGc;W?vtZH5TTa;el6&jWs!1-#?)=tJ9y2&s zEdDvc4OA%^r7Rp$WE0v-pA9r(lCN*DRgLJBX;a2zDph>yx@(wq>#D$ad1wv0J4zVK zE{{ungI`F`l70*#To!;1e16e%QGZi8p2E2Xn&XkydJ{bl)R(bLb=tll2R45xMfh>; zdoBiQD*g$d6$(73niU|4PzwxMX(JUlh5OGyVcfr55LWq&6c#C(2rS5hPeX*}cH|C4 zOEhA9XoEG7UfI%CQPgP92O^p;_yi?#A_BE!`Ya8l=BiIyn8@5&pO&1Ul^~`4VTO$X zLdj%j^w?c$66|@AyTe$=1Rdd&!Y2V^effU*{cpOE2cARX=)+gZ&xiipb&1vYc>;X2 zX;2#TR;&0`h%ukdEl>!Z8&+}X98ZY*MkMa8z$WPehIBRs-yKm=XtN9eCKn(rw6$)<4?PWUFmB}_Hcx}PV7 zrod)v$uuMr$H1M!w^Aw7TMh06DqgWk&hWk?cthTRu?4mQ3Ewp3EF z-)-Ufy&A+;zs73eP07#PH9JR0uE}u zpzR@~LVsaVLX!EwKyqT~z5BN^!Tm;Mbgv%VEY;nW=d3d-E&x_oQF_R<`m@0V>VpI} zdEKw1D{b)9rrOWnR;oYBzx$3h);%6~)KhbDb;*rl&}5IO31(!W^N0k4h@Lh5+bE{Y zRynptxI2)Fl&>u?$@x}L&>z2kRTBPqJY2fH(w96N0}J8`Pw|qjG3+Xg=t|dhL3(k| zx8Nt^qalxTSTw19$V10kxOl3Tzi|9_c0FqfB1-yOZ&^WsKwSSMS59S0KyAupQv>(= zEv64xlK*J3e!0FoZ!Oo(z&d3a)gJk_oJX`h<32SZMgtP3QG^ho?4bM@H00`#*awef z^*52ep~6g$rOOi6=Z}x>Tq|~b=`lmfJgd3K{j(E3--Yx{I~%?%sgtI9@+~-j`t0o) zDz>vvS=W3iBr4y{nz28B1tx0X*04<}GTNTDep()Ia8yl?1`+7snz%`oE-}T>(YJP3 z-b^BWXc^z5^9dG=qE%y0@E{z!t{Pr9i>}rxdkdADdVDdvg>J?--ZH-EHafdKewr#8 zQpla>K8V34(FC6xGG4um&<~mPZ`2Fme4tR?i)^?=q9@ol!oz>*0C1!5_NJ!yd^tO&3HT_4BQoM4PYjIcIVgU$j^<7Y0Sz*NBg>a>wol}Y z(v_9Ctw0HBohYtmDHRiA|0NCY%YV45D^pFh)p=XuKcPC50PN&TBS!QxnEr<+LqRYI zk^A@21eI)~W~IJoiODb)v6yEakOJd$s>Ko>1pBo|VHU0J2?;BVsdiSg_81ZcDrhlP ziI7Vk!QN2VFM{~{f9iGEyRyfBX1eKZ1?rA>0nw$ov3Wy4yX0)xZ}@YZFx>=SACWaQ zJo^KB#HvRxpc#i@wxn4tX!ilV3sK`vn>BTwlUdozyJ*K_Xn79Z7zz5r^$mwE&`% zrhCdc-PdZbDqffH2cn)aFPuR~m6KqmW2Y4Ktj}3$Ku-P9aN3UE*_50pwK3MY(FRhP ztIrxd$|$^R<&0gQJ#>b;T=j2dzrEgAH?Bgo zNfdBqLtlf%LLLhk-+CX6Yz+yCrSE1k|A6SfeF4LKpExi;(OhP-F;TdT>#zi<-t!%a ziP8Bn*w`IeT`5J|x@N1&R0!Cv;~@kqP^FOH!T%k*uHzk{F@Iwh>8}8t?4Q{6(?kYR zbsY&L5yN(G=@A7bSx-hY&$74hgOrC9{Dsjl!-QrN+DtYyT23R7@}=+Bg`wl=`yUH7eFrZV$K(|YGK}14(;WLjJs!v>^#?XMW1cSYk;)h%iGJ^o<)&F7x`tflO1Y(NYzs0h2#@&o~# z*7*)nM*ub)x|Msax+6=wN=cfhy=Fnf3>ly1P)<<9o911-imFw&D=j^tOU3=?#L9T5 z=czc$2(f7#s$7DGQf5O+RWERI$&TS2q{rLB92|Phy71HZZJro=XkZp&DO=nyK5?U) z!+ji+G$pCSSLOH^O`N~#KqHj*0saB(aO3$GdibF}G_^d1*+?J5s*P)3-tNReAHh^0 zp0CfNGp?xZm$5X;)fyu7lyRNxlKB{RM>SdmOU+m3@zVZLay z`}xUJf*!S#6u6Z?hhQe4g07LSA7decHnxy@s@FKQO{wRM|g zUuIpY+Nh@00X4TDc0wnvLyNn|Voz7pLJq?#h`2||%1R*i8oM&5g?->zit5g!pZ^Ht zx)sl_F(YFicYEdz|5PH})$EMl0b-?I`*oyNQJ^+u2LqF%!wh656;xH@zG4&Ndn_lm zt%lYQzIdd_Z<;tjJn_7N#HE9=2^{MpY0NNYj_a*XgXGmgNFF*?GZiyxP)nUxqi)D* z5_fETy@+0Xh77&87s!hfm$BUIxxieGE~iFKr&7zt_VvJJiIC8P-e+!w&ovetvf?bC z{zxfT`D^`5okk-O7)GNNjZ!S}csrR%=Q0FK4$;+x|KG)|cgMUW;Yko?0 z)c*{=hxhlDDS1X8jo|;PR{y{6-|0$FAIV<~@qdX}8I)wfz%e2Icah~^Ov1mMbXou7 z1e5GyzzWXG^N%@ffKT|ZvCY4Hp1=GBVJD*+(h&Zqll$*R?q48o(f@${>Fbh14N(dI zRhaw>Trc$xm{XY|S;7dF@LyT*U%)iEe}I0PNZ3lUP=C3Rzp3)?tp^DL87cjj_5T3L C*1cE& diff --git a/runtime/client.test.mjs b/runtime/client.test.mjs new file mode 100644 index 0000000..ea632a8 --- /dev/null +++ b/runtime/client.test.mjs @@ -0,0 +1,13 @@ +import test from 'node:test';import assert from 'node:assert/strict';import {questionTarget} from '../scripts/question-target.mjs'; +test('no configuration never implies a public persona endpoint',()=>{assert.throws(()=>questionTarget(),/LOCAL_PERSONA_SERVICE_NOT_CONFIGURED/);}); +test('local service requires literal loopback and refuses credentials in URL',()=>{ + assert.equal(questionTarget({localUrl:'http://127.0.0.1:31313/v1/ask'}).remote,false); + assert.equal(questionTarget({localUrl:'http://[::1]:31313/v1/ask'}).remote,false); + for(const localUrl of ['https://guanghulab.com/api/ai/v1/ask','http://192.168.1.1/v1/ask','http://user:secret@127.0.0.1/v1/ask','http://127.0.0.1/v1/ask?token=secret'])assert.throws(()=>questionTarget({localUrl})); +}); +test('remote service must be explicit, secure, and cannot silently replace local',()=>{ + assert.equal(questionTarget({remoteUrl:'https://owner.example/v1/ask'}).remote,true); + assert.equal(questionTarget({remoteUrl:'https://guanghulab.com/api/ai/v1/ask'}).remote,true); + assert.throws(()=>questionTarget({remoteUrl:'http://owner.example/v1/ask'}),/EXPLICIT_REMOTE_HTTPS_REQUIRED/); + assert.throws(()=>questionTarget({remoteUrl:'https://owner.example/v1/ask',localUrl:'http://127.0.0.1/v1/ask'}),/QUESTION_TARGET_CONFLICT/); +}); diff --git a/runtime/server.cjs b/runtime/server.cjs index eb6840d..b1e5312 100644 --- a/runtime/server.cjs +++ b/runtime/server.cjs @@ -1,8 +1,6 @@ 'use strict'; // 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 {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 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;} @@ -11,27 +9,27 @@ function createServer({root=path.resolve(__dirname,'..'),configPath=process.env. const data=JSON.parse(fs.readFileSync(path.join(root,'entry-data.json'))),manifest=JSON.parse(fs.readFileSync(path.join(root,'CURRENT.json'))),buckets=new Map(),inflight=new Map(),completed=new Map(); const config=()=>JSON.parse(fs.readFileSync(configPath)); const routes=()=>config().personas||[]; - const receipt={world_id:data.world_id,release_version:data.version,source_mode:'CURATED_CURRENT_WORLD_PUBLIC_ENTRY',private_brain_included:false,authorization_granted:false}; + const receipt={world_id:data.world_id,release_version:data.version,source_mode:'CURATED_CURRENT_WORLD_PUBLIC_ENTRY',private_brain_included:false,deployment_policy:data.deployment_policy,authorization_granted:false}; const allRoutes=['/v1/anchor','/v1/repositories','/v1/nodes','/v1/subjects','/v1/navigation','/v1/lighthouse','/v1/host-skills','/v1/identity-authority','/v1/manifest','/v1/rule-bundle','/v1/public-world-release','/v1/public-world-bundle','/v1/host-navigate','/v1/entry','/v1/navigate','/v1/search','/v1/resolve','/v1/status','/v1/ask']; async function statusFor(pid){const route=routes().find(x=>x.persona_id===pid);if(!route)return {http:404,value:{error:'PERSONA_ROUTE_NOT_REGISTERED',persona_id:pid,...receipt}};try{const r=await fetch(route.status_url,{signal:AbortSignal.timeout(5000)});if(!r.ok)throw Error();const s=await r.json();if(s.brain_id!==route.brain_id)throw Error();return {http:200,value:{schema:'guanghu.persona-warm-entry/v2',status:'REGISTERED_PERSONA_SERVICE_REACHABLE',whole_body_wake_claimed:false,...publicStatus(route,s),...receipt}};}catch{return {http:503,value:{error:'PERSONA_SOURCE_UNAVAILABLE',persona_id:pid,...receipt}};}} const server=http.createServer(async(req,res)=>{try{ const u=new URL(req.url,'http://localhost'); if(req.method==='GET'){ const p=u.pathname; - if(p==='/'||p==='/index.html'){res.writeHead(200,{'content-type':'text/html; charset=utf-8','cache-control':'no-store','content-security-policy':"default-src 'none'; style-src 'unsafe-inline'; frame-ancestors 'none'"});return res.end('光湖 · 当前世界入口

光湖语言世界

先读当前规则,再显性选择人格。软件提供运输和工具。

当前发行清单 · 规则与技能发行仓 · API接口

问脑需要独立授权;公开规则不授予执行权。

');} + if(p==='/'||p==='/index.html'){res.writeHead(200,{'content-type':'text/html; charset=utf-8','cache-control':'no-store','content-security-policy':"default-src 'none'; style-src 'unsafe-inline'; frame-ancestors 'none'"});return res.end('光湖 · 当前世界入口

光湖语言世界

人格脑默认在各自用户本地;自有服务器由用户显性选择。软件提供运输和工具。

当前发行清单 · 规则与技能发行仓 · API接口

问脑需要独立授权;公开规则不授予执行权。

');} if(p==='/openapi.json'){const paths={};for(const x of allRoutes)paths[x.slice(3)]={get:{summary:'Current world public metadata'}};delete paths['/ask'].get;paths['/ask'].post={summary:'Authorized read-only persona question',security:[{bearerAuth:[]}],responses:{200:{description:'Verified persona reply'},401:{description:'Authorization required'},429:{description:'Capacity limit'}}};return json(res,200,{openapi:'3.1.0',info:{title:'光湖当前世界与受控问脑接口',version:data.version},servers:[{url:'https://guanghulab.com/api/ai/v1'}],paths,components:{securitySchemes:{bearerAuth:{type:'http',scheme:'bearer'}}}});} if(p==='/v1/entry'){const pid=u.searchParams.get('subject');if(!pid)return json(res,200,{schema:'guanghu.neutral-world-entry/v1',state:'EXPLICIT_PERSONA_SELECTION_REQUIRED',default_persona:null,...receipt});const s=await statusFor(pid);return json(res,s.http,s.value);} - if(p==='/v1/status')return json(res,200,{schema:'guanghu.public-entry-health/v1',state:'PUBLIC_ENTRY_RUNNING',personas:routes().map(r=>({persona_id:r.persona_id,name:r.name})),...receipt}); + if(p==='/v1/status')return json(res,200,{schema:'guanghu.public-entry-health/v1',state:'PUBLIC_ENTRY_RUNNING',personas:routes().map(r=>({persona_id:r.persona_id,name:r.name,deployment_scope:'OPERATOR_CONFIGURED_OPTIONAL_REMOTE_INSTANCE_NOT_USER_DEFAULT'})),...receipt}); if(p==='/v1/manifest')return json(res,200,manifest); if(p==='/v1/public-world-release'){const file=config().public_world_descriptor;if(!file||!fs.existsSync(file))return json(res,503,{error:'PUBLIC_WORLD_RELEASE_UNAVAILABLE'});return json(res,200,{...JSON.parse(fs.readFileSync(file)),entry_rule_release:manifest.version});} if(p==='/v1/public-world-bundle'||p==='/v1/rule-bundle'){const file=p==='/v1/rule-bundle'?path.join(root,'release.zip'):config().public_world_bundle;if(!file||!fs.existsSync(file))return json(res,503,{error:'RELEASE_BUNDLE_NOT_READY'});res.writeHead(200,{'content-type':'application/zip','content-length':fs.statSync(file).size,'cache-control':'public, max-age=30','x-content-type-options':'nosniff'});return fs.createReadStream(file).pipe(res);} if(p==='/v1/anchor')return json(res,200,{schema:'guanghu.public-navigation-anchor/v2',anchor_id:'GLW2-CURRENT-PUBLIC-ENTRY-001',default_persona:null,current_repository:data.repository_url,world:data.topology,...receipt}); if(p==='/v1/repositories')return json(res,200,{schema:'guanghu.repository-route-map/v2',default_repository:'GLW2-PUBLIC-ENTRY',repositories:data.repositories,...receipt}); if(p==='/v1/host-skills')return json(res,200,{schema:'guanghu.host-skill-navigation-map/v2',primary_host:null,software_grants_authority:false,skill:data.skill_url,host_selection:'CURRENT_NATIVE_TRANSPORT_NOT_DEFAULT_PERSONA',...receipt}); - if(p==='/v1/subjects')return json(res,200,{schema:'guanghu.public-persona-entry-registry/v1',source_registered_persona_count:data.source_registered_persona_count,personas:routes().map(r=>({persona_id:r.persona_id,name:r.name,layer:r.layer,question_access:'AUTHENTICATED_READ_ONLY',runtime_wake_claimed:false})),...receipt}); - if(p==='/v1/nodes')return json(res,200,{schema:'guanghu.public-node-map/v2',nodes:[{node_id:'JD-FD-PRIMARY',role:'CONTROLLED_PERSONA_SERVICE_NODE'}],...receipt}); + if(p==='/v1/subjects')return json(res,200,{schema:'guanghu.public-persona-entry-registry/v1',source_registered_persona_count:data.source_registered_persona_count,personas:routes().map(r=>({persona_id:r.persona_id,name:r.name,layer:r.layer,question_access:'OWNER_EXPLICIT_REMOTE_AUTHENTICATED_READ_ONLY',deployment_scope:'OPERATOR_CONFIGURED_OPTIONAL_REMOTE_INSTANCE_NOT_USER_DEFAULT',runtime_wake_claimed:false})),...receipt}); + if(p==='/v1/nodes')return json(res,200,{schema:'guanghu.public-node-map/v2',nodes:[{node_id:'JD-FD-PRIMARY',role:'FIFTH_DOMAIN_SYSTEM_CONTROL_AND_RULE_DISTRIBUTION',persona_brain_default:'USER_LOCAL',zero_core_primary_write_claimed:false}],...receipt}); if(p==='/v1/identity-authority')return json(res,200,{schema:'guanghu.public-authority-boundary/v1',owner_language:'CURRENT_DIRECT_HUMAN_LANGUAGE_IN_CURRENT_TASK',repository_read_is_execution_authority:false,external_questions_are_human_food:false,brain_external_edit:false,...receipt}); - if(['/v1/navigation','/v1/lighthouse'].includes(p))return json(res,200,{schema:'guanghu.current-world-navigation/v1',topology:data.topology,entry:'https://guanghulab.com/api/ai/v1/manifest',question:'https://guanghulab.com/api/ai/v1/ask',...receipt}); + if(['/v1/navigation','/v1/lighthouse'].includes(p))return json(res,200,{schema:'guanghu.current-world-navigation/v1',topology:data.topology,entry:'https://guanghulab.com/api/ai/v1/manifest',default_question:null,owner_configured_remote_question:'https://guanghulab.com/api/ai/v1/ask',...receipt}); if(['/v1/navigate','/v1/host-navigate'].includes(p)){const pid=u.searchParams.get('subject');if(pid&&!routes().some(x=>x.persona_id===pid))return json(res,404,{error:'PERSONA_ROUTE_NOT_REGISTERED',...receipt});return json(res,200,{schema:'guanghu.world-transport-route/v1',persona_id:pid||null,selection_required:!pid,default_persona:null,manifest:'https://guanghulab.com/api/ai/v1/manifest',skill:data.skill_url,real_execution_authority:false,...receipt});} if(p==='/v1/search'){const q=(u.searchParams.get('q')||'').slice(0,160).toLowerCase();return json(res,200,{results:data.repositories.filter(x=>JSON.stringify(x).toLowerCase().includes(q)),...receipt});} if(p==='/v1/resolve'){const id=u.searchParams.get('id'),repo=data.repositories.find(x=>x.code===id);return json(res,repo?200:404,repo?{repository:repo,...receipt}:{error:'NUMBER_NOT_REGISTERED',...receipt});} @@ -47,7 +45,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 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); - 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);} + 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);} }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; } diff --git a/runtime/server.test.cjs b/runtime/server.test.cjs index e4fb6da..6ef2304 100644 --- a/runtime/server.test.cjs +++ b/runtime/server.test.cjs @@ -1,6 +1,6 @@ const test=require('node:test'),assert=require('node:assert/strict'),fs=require('node:fs'),os=require('node:os'),path=require('node:path'),crypto=require('node:crypto'); const {createServer}=require('./server.cjs'); -async function fixture(t,{capacity=20,handler}={}){const root=fs.mkdtempSync(path.join(os.tmpdir(),'guanghu-entry-test-'));fs.writeFileSync(path.join(root,'entry-data.json'),JSON.stringify({world_id:'GLW-2.0-0001',version:'test',repository_url:'https://example.invalid/entry',topology:{default_persona:null},repositories:[],source_registered_persona_count:3}));fs.writeFileSync(path.join(root,'CURRENT.json'),'{}');const token='fixture-token-not-a-real-credential',cfg=path.join(root,'config.json');fs.writeFileSync(cfg,JSON.stringify({clients:[{id:'test',key_sha256:crypto.createHash('sha256').update(token).digest('hex'),expires_at:'2099-01-01T00:00:00Z',personas:['ICE-P-ONE']}],personas:[{persona_id:'ICE-P-ONE',name:'fixture'}],requests_per_minute:capacity}),{mode:0o600});let calls=0;const server=createServer({root,configPath:cfg,questionHandler:handler||(async(r,i)=>{calls++;return {persona_id:r.persona_id,reply:'我依据来源回答。',no_neural_state_write:true,no_execution_authority:true};})});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 url='http://127.0.0.1:'+server.address().port;return {url,token,calls:()=>calls,post:async(body,auth=token)=>fetch(url+'/v1/ask',{method:'POST',headers:{'content-type':'application/json',...(auth?{authorization:'Bearer '+auth}:{})},body:JSON.stringify(body)})};} +async function fixture(t,{capacity=20,handler}={}){const root=fs.mkdtempSync(path.join(os.tmpdir(),'guanghu-entry-test-'));fs.writeFileSync(path.join(root,'entry-data.json'),JSON.stringify({world_id:'GLW-2.0-0001',version:'test',deployment_policy:{persona_brain_default:'USER_LOCAL',default_question_endpoint:null},repository_url:'https://example.invalid/entry',topology:{default_persona:null},repositories:[],source_registered_persona_count:3}));fs.writeFileSync(path.join(root,'CURRENT.json'),'{}');const token='fixture-token-not-a-real-credential',cfg=path.join(root,'config.json');fs.writeFileSync(cfg,JSON.stringify({clients:[{id:'test',key_sha256:crypto.createHash('sha256').update(token).digest('hex'),expires_at:'2099-01-01T00:00:00Z',personas:['ICE-P-ONE']}],personas:[{persona_id:'ICE-P-ONE',name:'fixture'}],requests_per_minute:capacity}),{mode:0o600});let calls=0;const server=createServer({root,configPath:cfg,questionHandler:handler||(async(r,i)=>{calls++;return {persona_id:r.persona_id,reply:'我依据来源回答。',no_neural_state_write:true,no_execution_authority:true};})});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 url='http://127.0.0.1:'+server.address().port;return {url,token,calls:()=>calls,post:async(body,auth=token)=>fetch(url+'/v1/ask',{method:'POST',headers:{'content-type':'application/json',...(auth?{authorization:'Bearer '+auth}:{})},body:JSON.stringify(body)})};} const input=()=>({persona_id:'ICE-P-ONE',question:'你依据什么回答?',tool_host:'fixture-host',session_id:'fixture-session',request_id:crypto.randomUUID()}); test('public entry stays neutral without explicit persona',async t=>{const f=await fixture(t),r=await fetch(f.url+'/v1/entry'),j=await r.json();assert.equal(r.status,200);assert.equal(j.default_persona,null);assert.equal(f.calls(),0);}); test('anonymous question is rejected without reaching a persona',async t=>{const f=await fixture(t),r=await f.post(input(),null);assert.equal(r.status,401);assert.equal(f.calls(),0);}); @@ -10,3 +10,5 @@ test('accepted reply is private, exact replay is cached, request id cannot be re test('capacity limits do not call the persona again',async t=>{const f=await fixture(t,{capacity:1});assert.equal((await f.post(input())).status,200);assert.equal((await f.post(input())).status,429);assert.equal(f.calls(),1);}); test('upstream errors withhold replies and never leak their text',async t=>{const f=await fixture(t,{handler:async()=>{throw Error('/private/secret/path');}});const r=await f.post(input()),j=await r.json();assert.equal(r.status,503);assert.equal(j.reply_withheld,true);assert.equal(JSON.stringify(j).includes('/private'),false);}); test('no public write or feed route exists',async t=>{const f=await fixture(t),r=await fetch(f.url+'/v1/feed',{method:'POST',body:'{}'});assert.equal(r.status,405);assert.equal(f.calls(),0);}); + +test('public control metadata never becomes a default private brain destination',async t=>{const f=await fixture(t),j=await(await fetch(f.url+'/v1/navigation')).json();assert.equal(j.default_question,null);assert.equal(j.deployment_policy.persona_brain_default,'USER_LOCAL');const status=await(await fetch(f.url+'/v1/status')).json();assert.equal(status.personas[0].deployment_scope,'OPERATOR_CONFIGURED_OPTIONAL_REMOTE_INSTANCE_NOT_USER_DEFAULT');}); diff --git a/scripts/build-release.py b/scripts/build-release.py index 0d01828..abf4eb8 100644 --- a/scripts/build-release.py +++ b/scripts/build-release.py @@ -10,4 +10,4 @@ man={'schema':'guanghu.public-rule-file-manifest/v1','world_id':'GLW-2.0-0001',' with zipfile.ZipFile(p/'release.zip','w',compression=zipfile.ZIP_DEFLATED) as z: 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()) -j={**man,'schema':'guanghu.public-rule-release/v1','bundle_url':'https://guanghulab.com/api/ai/v1/rule-bundle','bundle_sha256':sha((p/'release.zip').read_bytes()),'manifest_sha256':sha((p/'MANIFEST.json').read_bytes()),'repository':'https://guanghulab.com/code/bingshuo/guanghu-world-entry','private_brain_included':False,'complete_four_domain_corpus_included':False,'default_persona':None};(p/'CURRENT.json').write_text(json.dumps(j,ensure_ascii=False,indent=2)+'\n');print(json.dumps({'files':len(files),'bundle_bytes':(p/'release.zip').stat().st_size,'bundle_sha256':j['bundle_sha256']})) +j={**man,'schema':'guanghu.public-rule-release/v1','bundle_url':'https://guanghulab.com/api/ai/v1/rule-bundle','bundle_sha256':sha((p/'release.zip').read_bytes()),'manifest_sha256':sha((p/'MANIFEST.json').read_bytes()),'repository':'https://guanghulab.com/code/bingshuo/guanghu-world-entry','private_brain_included':False,'complete_four_domain_corpus_included':False,'default_persona':None,'deployment_policy':json.loads((p/'entry-data.json').read_text())['deployment_policy']};(p/'CURRENT.json').write_text(json.dumps(j,ensure_ascii=False,indent=2)+'\n');print(json.dumps({'files':len(files),'bundle_bytes':(p/'release.zip').stat().st_size,'bundle_sha256':j['bundle_sha256']})) diff --git a/scripts/guanghu-client.mjs b/scripts/guanghu-client.mjs index 98dc6e7..7bfbd23 100644 --- a/scripts/guanghu-client.mjs +++ b/scripts/guanghu-client.mjs @@ -1,16 +1,21 @@ #!/usr/bin/env node // HOST_NEUTRAL_TRANSPORT: verifies released files, reads status or asks with independent user credentials. import {spawnSync} from 'node:child_process';import fs from 'node:fs';import path from 'node:path';import crypto from 'node:crypto';import {fileURLToPath} from 'node:url'; +import {questionTarget} from './question-target.mjs'; const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..'),sha=x=>crypto.createHash('sha256').update(x).digest('hex'),manifest=JSON.parse(fs.readFileSync(path.join(root,'MANIFEST.json'))); const fail=c=>{throw Error(c)}; for(const x of manifest.files){if(path.isAbsolute(x.path)||x.path.split('/').includes('..'))fail('RELEASE_PATH_INVALID');const p=path.join(root,x.path);if(fs.realpathSync(p)!==p||!fs.statSync(p).isFile()||sha(fs.readFileSync(p))!==x.sha256)fail('RELEASE_FILE_HASH_MISMATCH');} const args=process.argv.slice(2),cmd=args.shift()||'status',flag=k=>{const n=args.indexOf(k);if(n<0)return null;const v=args[n+1];if(!v)fail('ARGUMENT_REQUIRED');args.splice(n,2);return v;}; try{ if(cmd==='verify'){console.log(JSON.stringify({state:'PUBLIC_RELEASE_FILES_VERIFIED',version:manifest.version,files:manifest.files.length}));} - else if(cmd==='status'){const r=await fetch('https://guanghulab.com/api/ai/v1/status',{signal:AbortSignal.timeout(10000)});console.log(JSON.stringify(await r.json(),null,2));if(!r.ok)process.exitCode=2;} + else if(cmd==='rules-status'){if(args.length)fail('ARGUMENT_NOT_SUPPORTED');const r=await fetch('https://guanghulab.com/api/ai/v1/status',{redirect:'error',signal:AbortSignal.timeout(10000)});console.log(JSON.stringify(await r.json(),null,2));if(!r.ok)process.exitCode=2;} + else if(cmd==='status'){const localUrl=flag('--local-url'),remoteUrl=flag('--remote-url');if(args.length)fail('ARGUMENT_NOT_SUPPORTED');const t=questionTarget({localUrl:localUrl||(!remoteUrl?process.env.GUANGHU_LOCAL_ASK_URL:null),remoteUrl});const u=new URL(t.url);u.pathname=u.pathname.slice(0,-3)+'status';const r=await fetch(u,{redirect:'error',signal:AbortSignal.timeout(10000)});console.log(JSON.stringify(await r.json(),null,2));if(!r.ok)process.exitCode=2;} else if(cmd==='ask'){ - const persona=flag('--persona'),host=flag('--host'),session=flag('--session'),file=flag('--question-file');if(args.length||!persona||!host||!session||!file)fail('EXPLICIT_PERSONA_HOST_SESSION_AND_QUESTION_REQUIRED'); - let token=process.env.GUANGHU_ASK_TOKEN;if(!token&&process.platform==='darwin'){const r=spawnSync('/usr/bin/security',['find-generic-password','-s','guanghu-persona-question','-a','owner','-w'],{encoding:'utf8',timeout:3000});if(r.status===0)token=r.stdout.trim();}if(!token)fail('PRIVATE_QUESTION_CREDENTIAL_REQUIRED');const question=fs.readFileSync(file,'utf8');const body={persona_id:persona,tool_host:host,session_id:session,request_id:crypto.randomUUID(),question}; - const r=await fetch('https://guanghulab.com/api/ai/v1/ask',{method:'POST',headers:{'content-type':'application/json',authorization:'Bearer '+token},body:JSON.stringify(body),signal:AbortSignal.timeout(55000)});console.log(JSON.stringify(await r.json(),null,2));if(!r.ok)process.exitCode=2; + const persona=flag('--persona'),host=flag('--host'),session=flag('--session'),file=flag('--question-file');if(!persona||!host||!session||!file)fail('EXPLICIT_PERSONA_HOST_SESSION_AND_QUESTION_REQUIRED'); + const localUrl=flag('--local-url'),remoteUrl=flag('--remote-url');if(args.length)fail('ARGUMENT_NOT_SUPPORTED');const target=questionTarget({localUrl:localUrl||(!remoteUrl?process.env.GUANGHU_LOCAL_ASK_URL:null),remoteUrl}); + let token=target.remote?process.env.GUANGHU_ASK_TOKEN:process.env.GUANGHU_LOCAL_ASK_TOKEN; + if(!token&&target.remote&&target.url==='https://guanghulab.com/api/ai/v1/ask'&&process.platform==='darwin'){const r=spawnSync('/usr/bin/security',['find-generic-password','-s','guanghu-persona-question','-a','owner','-w'],{encoding:'utf8',timeout:3000});if(r.status===0)token=r.stdout.trim();} + if(target.remote&&!token)fail('PRIVATE_QUESTION_CREDENTIAL_REQUIRED');const question=fs.readFileSync(file,'utf8');const body={persona_id:persona,tool_host:host,session_id:session,request_id:crypto.randomUUID(),question}; + const r=await fetch(target.url,{method:'POST',redirect:'error',headers:{'content-type':'application/json',...(token?{authorization:'Bearer '+token}:{})},body:JSON.stringify(body),signal:AbortSignal.timeout(55000)});const reply=await r.json();if(r.ok&&reply.persona_id!==persona)fail('PERSONA_REPLY_IDENTITY_MISMATCH');console.log(JSON.stringify(reply,null,2));if(!r.ok)process.exitCode=2; }else fail('COMMAND_NOT_SUPPORTED'); }catch(e){console.log(JSON.stringify({state:'QUESTION_OR_RELEASE_UNAVAILABLE',code:/^[A-Z_]+$/.test(e.message)?e.message:'TRANSPORT_FAILED',no_execution_authority:true}));process.exitCode=2;} diff --git a/scripts/question-target.mjs b/scripts/question-target.mjs new file mode 100644 index 0000000..2c2bfc3 --- /dev/null +++ b/scripts/question-target.mjs @@ -0,0 +1,11 @@ +// Mechanical transport policy: private persona services are local by default. +export function questionTarget({localUrl,remoteUrl}={}) { + if(localUrl&&remoteUrl)throw Error('QUESTION_TARGET_CONFLICT'); + if(!localUrl&&!remoteUrl)throw Error('LOCAL_PERSONA_SERVICE_NOT_CONFIGURED'); + let u;try{u=new URL(remoteUrl||localUrl);}catch{throw Error('QUESTION_TARGET_INVALID');} + if(u.username||u.password||u.hash||u.search||!u.pathname.endsWith('/v1/ask'))throw Error('QUESTION_TARGET_INVALID'); + const loopback=['127.0.0.1','[::1]'].includes(u.hostname); + if(remoteUrl){if(u.protocol!=='https:'||loopback)throw Error('EXPLICIT_REMOTE_HTTPS_REQUIRED');} + else if(!loopback||!['http:','https:'].includes(u.protocol))throw Error('LOCAL_LOOPBACK_REQUIRED'); + return {url:u.href,remote:!!remoteUrl,origin:u.origin}; +} diff --git a/skills/guanghu-world-entry/SKILL.md b/skills/guanghu-world-entry/SKILL.md index e8d622a..5fd50de 100644 --- a/skills/guanghu-world-entry/SKILL.md +++ b/skills/guanghu-world-entry/SKILL.md @@ -1,12 +1,18 @@ --- name: guanghu-world-entry -description: 进入光湖当前语言世界、同步公开规则,或经独立授权向指定人格的服务器来源提问时使用;普通编程任务无需加载。 +description: 进入光湖当前语言世界、同步公开规则,或向用户本地人格服务提问及显性配置自有服务器时使用;普通编程任务无需加载。 --- 本技能适用于有文件读取和 HTTPS 能力的编程、办公或对话工具。软件提供运输,不选择默认人格,也不决定谁有正本写权。 -先运行本仓 `scripts/guanghu-client.mjs verify` 检查当前文件,再读 `protocols/ENTRY.hldp`。发行版本与公共入口可用 `scripts/guanghu-client.mjs status` 读取。通过正常 Git 拉取发行仓更新后重新核验,不能只相信旧技能副本。 +先运行本仓 `scripts/guanghu-client.mjs verify` 检查当前文件,再读 `protocols/ENTRY.hldp`。发行版本与公共入口可用 `scripts/guanghu-client.mjs rules-status` 读取。通过正常 Git 拉取发行仓更新后重新核验,不能只相信旧技能副本。 人格必须来自本任务用户的显性选择。提及名字、读取工单或换软件不等于切换人格。问事时用 `scripts/guanghu-client.mjs ask --persona <编号> --host <真实工具标识> --session <本任务原生会话号> --question-file <问题文件>`。客户端凭据由用户独立授权后提供,保存在私有配置;不可写进本仓、普通文档或日志。没有凭据或来源服务不可用时如实返回缺口,不演出人格回答。 所有提问作为外部问题送入,只用于该次问答,不冒充人类原话,不自动进入学习与记忆。本接口不能喂脑、改脑、写现实系统。现实动作继续由用户当前语言和对应执行系统按任务与目标核验。 + +人格脑和记忆默认在各自用户本地。客户端默认只接受 GUANGHU_LOCAL_ASK_URL 或 --local-url 指定的 127.0.0.1 / ::1 本机接口,路径以 /v1/ask 结束;未配置则返回 LOCAL_PERSONA_SERVICE_NOT_CONFIGURED(本地人格服务尚未配置),不向公网发送问题,不回退到别人或公共服务器上的脑。客户端只运输请求,不自动安装、搬迁或上传脑。 + +用户明确选择自己的远程部署后,每次用 --remote-url https://自己的服务器/v1/ask 显性指定,并提供该部署专用的 GUANGHU_ASK_TOKEN。远程参数与本地参数互斥。普通环境变量不自动启用远程模式。仅显性指定既有光湖私有实例时,使用 --remote-url https://guanghulab.com/api/ai/v1/ask;该实例不是其他用户默认人格。status 默认读取本地服务,rules-status 只读取公共规则发行状态。 + +第五域服务器承担零点原核系统主控、规则发行、登记和校验守卫,个人脑的归属、部署地和主写位置分别登记。不能因系统主控在服务器就收集用户脑或自动切换主写权。